Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions arch/arm64/boot/dts/qcom/lemans-evk.dts
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,24 @@
status = "okay";
};

&q6apmbedai {
dai@16 {
reg = <PRIMARY_MI2S_RX>;
clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
<&q6prmcc LPASS_CLK_ID_PRI_MI2S_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
<&q6prmcc LPASS_CLK_ID_PRI_MI2S_EBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
clock-names = "mclk", "bclk", "eclk";
};

dai@21 {
reg = <TERTIARY_MI2S_TX>;
clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
<&q6prmcc LPASS_CLK_ID_TER_MI2S_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
<&q6prmcc LPASS_CLK_ID_TER_MI2S_EBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
clock-names = "mclk", "bclk", "eclk";
};
};

&qupv3_id_0 {
status = "okay";
};
Expand Down
149 changes: 147 additions & 2 deletions sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/pcm.h>
Expand All @@ -15,13 +17,23 @@
#include "q6dsp-common.h"
#include "audioreach.h"
#include "q6apm.h"
#include "q6prm.h"

#define AUDIOREACH_BE_PCM_BASE 16

struct q6apm_dai_priv_data {
struct clk *mclk;
struct clk *bclk;
struct clk *eclk;
bool mclk_enabled, bclk_enabled, eclk_enabled;
};

struct q6apm_lpass_dai_data {
struct q6apm_graph *graph[APM_PORT_MAX];
bool is_port_started[APM_PORT_MAX];
struct audioreach_module_config module_config[APM_PORT_MAX];
struct q6apm_lpass_clk_data *clk_data[APM_PORT_MAX];
struct q6apm_dai_priv_data priv[APM_PORT_MAX];
};

static int q6dma_set_channel_map(struct snd_soc_dai *dai,
Expand Down Expand Up @@ -159,6 +171,27 @@ static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct
}
}

static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);

if (dai_data->priv[dai->id].mclk_enabled) {
clk_disable_unprepare(dai_data->priv[dai->id].mclk);
dai_data->priv[dai->id].mclk_enabled = false;
}

if (dai_data->priv[dai->id].bclk_enabled) {
clk_disable_unprepare(dai_data->priv[dai->id].bclk);
dai_data->priv[dai->id].bclk_enabled = false;
}

if (dai_data->priv[dai->id].eclk_enabled) {
clk_disable_unprepare(dai_data->priv[dai->id].eclk);
dai_data->priv[dai->id].eclk_enabled = false;
}
q6apm_lpass_dai_shutdown(substream, dai);
}

static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
Expand Down Expand Up @@ -238,6 +271,11 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s
return 0;
}

static int q6i2s_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
return q6apm_lpass_dai_startup(substream, dai);
}

static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
Expand All @@ -248,6 +286,52 @@ static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}

static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir)
{
struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev);
struct clk *sysclk;
bool *enabled;
int ret = 0;

switch (clk_id) {
case Q6PRM_LPASS_CLK_ID_MCLK_1...Q6PRM_LPASS_CLK_ID_MCLK_5:
sysclk = dai_data->priv[dai->id].mclk;
enabled = &dai_data->priv[dai->id].mclk_enabled;
break;
case Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT:
case Q6PRM_LPASS_CLK_ID_SEC_MI2S_IBIT:
case Q6PRM_LPASS_CLK_ID_TER_MI2S_IBIT:
case Q6PRM_LPASS_CLK_ID_QUAD_MI2S_IBIT:
case Q6PRM_LPASS_CLK_ID_QUI_MI2S_IBIT:
sysclk = dai_data->priv[dai->id].bclk;
enabled = &dai_data->priv[dai->id].bclk_enabled;
break;
case Q6PRM_LPASS_CLK_ID_PRI_MI2S_EBIT:
case Q6PRM_LPASS_CLK_ID_SEC_MI2S_EBIT:
case Q6PRM_LPASS_CLK_ID_TER_MI2S_EBIT:
case Q6PRM_LPASS_CLK_ID_QUAD_MI2S_EBIT:
case Q6PRM_LPASS_CLK_ID_QUI_MI2S_EBIT:
sysclk = dai_data->priv[dai->id].eclk;
enabled = &dai_data->priv[dai->id].eclk_enabled;
break;
default:
break;
}

if (sysclk) {
clk_set_rate(sysclk, freq);
ret = clk_prepare_enable(sysclk);
if (ret) {
dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id);
return ret;
}

*enabled = true;
}

return ret;
}

static const struct snd_soc_dai_ops q6dma_ops = {
.prepare = q6apm_lpass_dai_prepare,
.startup = q6apm_lpass_dai_startup,
Expand All @@ -258,11 +342,12 @@ static const struct snd_soc_dai_ops q6dma_ops = {

static const struct snd_soc_dai_ops q6i2s_ops = {
.prepare = q6apm_lpass_dai_prepare,
.startup = q6apm_lpass_dai_startup,
.shutdown = q6apm_lpass_dai_shutdown,
.startup = q6i2s_dai_startup,
.shutdown = q6i2s_lpass_dai_shutdown,
.set_channel_map = q6dma_set_channel_map,
.hw_params = q6dma_hw_params,
.set_fmt = q6i2s_set_fmt,
.set_sysclk = q6i2s_set_sysclk,
};

static const struct snd_soc_dai_ops q6hdmi_ops = {
Expand All @@ -279,20 +364,80 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = {
.be_pcm_base = AUDIOREACH_BE_PCM_BASE,
.use_dai_pcm_id = true,
};
static int of_q6apm_parse_dai_data(struct device *dev,
struct q6apm_lpass_dai_data *data)
{
struct device_node *node;
int ret;
for_each_child_of_node(dev->of_node, node) {
struct q6apm_dai_priv_data *priv;
int id, i;

ret = of_property_read_u32(node, "reg", &id);
if (ret || id < 0 || id >= APM_PORT_MAX) {
dev_err(dev, "valid dai id not found:%d\n", ret);
continue;
}

switch (id) {
/* MI2S specific properties */
case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX:
case QUINARY_MI2S_RX ... QUINARY_MI2S_TX:
priv = &data->priv[id];
priv->mclk = of_clk_get_by_name(node, "mclk");
if (IS_ERR(priv->mclk)) {
if(PTR_ERR(priv->mclk) == -ENOENT) {
dev_err_probe(dev, PTR_ERR(priv->mclk), "unable to get mi2s mclk\n");
return -EPROBE_DEFER;
}
priv->mclk = NULL;
}

priv->bclk = of_clk_get_by_name(node, "bclk");
if (IS_ERR(priv->bclk)) {
if(PTR_ERR(priv->bclk) == -ENOENT) {
dev_err_probe(dev, PTR_ERR(priv->bclk), "unable to get mi2s bclk\n");
return -EPROBE_DEFER;
}

priv->bclk = NULL;
}

priv->eclk = of_clk_get_by_name(node, "eclk");
if (IS_ERR(priv->eclk)) {
if(PTR_ERR(priv->eclk) == -ENOENT) {
dev_err_probe(dev, PTR_ERR(priv->eclk), "unable to get mi2s eclk\n");
return -EPROBE_DEFER;
}

priv->eclk = NULL;

}
break;
default:
break;
}
}

return 0;
}
static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev)
{
struct q6dsp_audio_port_dai_driver_config cfg;
struct q6apm_lpass_dai_data *dai_data;
struct snd_soc_dai_driver *dais;
struct device *dev = &pdev->dev;
int num_dais;
int ret;

dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL);
if (!dai_data)
return -ENOMEM;

dev_set_drvdata(dev, dai_data);
ret = of_q6apm_parse_dai_data(dev, dai_data);
if (ret)
return ret;

memset(&cfg, 0, sizeof(cfg));
cfg.q6i2s_ops = &q6i2s_ops;
Expand Down
25 changes: 25 additions & 0 deletions sound/soc/qcom/sc8280xp.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <sound/jack.h>
#include <linux/input-event-codes.h>
#include "qdsp6/q6afe.h"
#include "qdsp6/q6prm.h"
#include "common.h"
#include "sdw.h"

Expand Down Expand Up @@ -114,6 +115,30 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card);
int ret = 0;

switch (cpu_dai->id) {
case PRIMARY_MI2S_RX...PRIMARY_MI2S_TX:
ret = snd_soc_dai_set_sysclk(cpu_dai, Q6PRM_LPASS_CLK_ID_MCLK_1, 12288000, SND_SOC_CLOCK_IN);
break;
case SECONDARY_MI2S_RX...SECONDARY_MI2S_TX:
ret = snd_soc_dai_set_sysclk(cpu_dai, Q6PRM_LPASS_CLK_ID_MCLK_2, 12288000, SND_SOC_CLOCK_IN);
break;
case TERTIARY_MI2S_RX...TERTIARY_MI2S_TX:
ret = snd_soc_dai_set_sysclk(cpu_dai, Q6PRM_LPASS_CLK_ID_MCLK_3, 12288000, SND_SOC_CLOCK_IN);
break;
case QUATERNARY_MI2S_RX...QUATERNARY_MI2S_TX:
ret = snd_soc_dai_set_sysclk(cpu_dai, Q6PRM_LPASS_CLK_ID_MCLK_4, 12288000, SND_SOC_CLOCK_IN);
break;
case QUINARY_MI2S_RX...QUINARY_MI2S_TX:
ret = snd_soc_dai_set_sysclk(cpu_dai, Q6PRM_LPASS_CLK_ID_MCLK_5, 12288000, SND_SOC_CLOCK_IN);
break;
default:
break;
}

if (ret < 0)
dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret);

return qcom_snd_sdw_hw_params(substream, params, &pdata->sruntime[cpu_dai->id]);
}
Expand Down