ASoC: fsl-asoc-card: add compatibility to use 2 codecs in dai-links

Adapt the driver to work with configurations using two codecs or more.
Modify fsl_asoc_card_probe() to handle use cases where 2 codecs are
given in the device tree.
This will be needed to add support for the SPDIF.

Use cases using one codec will ignore any given codecs other than the
first.

Co-developed-by: Philip-Dylan Gleonec <philip-dylan.gleonec@savoirfairelinux.com>
Signed-off-by: Philip-Dylan Gleonec <philip-dylan.gleonec@savoirfairelinux.com>
Signed-off-by: Elinor Montmasson <elinor.montmasson@savoirfairelinux.com>
Link: https://patch.msgid.link/20240627083104.123357-4-elinor.montmasson@savoirfairelinux.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Elinor Montmasson 2024-06-27 10:31:00 +02:00 committed by Mark Brown
parent c68fa0d9b0
commit fcc6ace84f
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0

View File

@ -99,7 +99,7 @@ struct fsl_asoc_card_priv {
struct simple_util_jack hp_jack;
struct simple_util_jack mic_jack;
struct platform_device *pdev;
struct codec_priv codec_priv;
struct codec_priv codec_priv[2];
struct cpu_priv cpu_priv;
struct snd_soc_card card;
u8 streams;
@ -172,10 +172,12 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
struct codec_priv *codec_priv = &priv->codec_priv;
struct codec_priv *codec_priv;
struct snd_soc_dai *codec_dai;
struct cpu_priv *cpu_priv = &priv->cpu_priv;
struct device *dev = rtd->card->dev;
unsigned int pll_out;
int codec_idx;
int ret;
priv->sample_rate = params_rate(params);
@ -208,28 +210,32 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
}
/* Specific configuration for PLL */
if (codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) {
if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
pll_out = priv->sample_rate * 384;
else
pll_out = priv->sample_rate * 256;
for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) {
codec_priv = &priv->codec_priv[codec_idx];
ret = snd_soc_dai_set_pll(snd_soc_rtd_to_codec(rtd, 0),
codec_priv->pll_id,
codec_priv->mclk_id,
codec_priv->mclk_freq, pll_out);
if (ret) {
dev_err(dev, "failed to start FLL: %d\n", ret);
goto fail;
}
if (codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) {
if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
pll_out = priv->sample_rate * 384;
else
pll_out = priv->sample_rate * 256;
ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0),
codec_priv->fll_id,
pll_out, SND_SOC_CLOCK_IN);
ret = snd_soc_dai_set_pll(codec_dai,
codec_priv->pll_id,
codec_priv->mclk_id,
codec_priv->mclk_freq, pll_out);
if (ret) {
dev_err(dev, "failed to start FLL: %d\n", ret);
goto fail;
}
if (ret && ret != -ENOTSUPP) {
dev_err(dev, "failed to set SYSCLK: %d\n", ret);
goto fail;
ret = snd_soc_dai_set_sysclk(codec_dai,
codec_priv->fll_id,
pll_out, SND_SOC_CLOCK_IN);
if (ret && ret != -ENOTSUPP) {
dev_err(dev, "failed to set SYSCLK: %d\n", ret);
goto fail;
}
}
}
@ -244,28 +250,34 @@ static int fsl_asoc_card_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
struct codec_priv *codec_priv = &priv->codec_priv;
struct codec_priv *codec_priv;
struct snd_soc_dai *codec_dai;
struct device *dev = rtd->card->dev;
int codec_idx;
int ret;
priv->streams &= ~BIT(substream->stream);
if (!priv->streams && codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) {
/* Force freq to be free_freq to avoid error message in codec */
ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(rtd, 0),
codec_priv->mclk_id,
codec_priv->free_freq,
SND_SOC_CLOCK_IN);
if (ret) {
dev_err(dev, "failed to switch away from FLL: %d\n", ret);
return ret;
}
for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) {
codec_priv = &priv->codec_priv[codec_idx];
ret = snd_soc_dai_set_pll(snd_soc_rtd_to_codec(rtd, 0),
codec_priv->pll_id, 0, 0, 0);
if (ret && ret != -ENOTSUPP) {
dev_err(dev, "failed to stop FLL: %d\n", ret);
return ret;
if (!priv->streams && codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) {
/* Force freq to be free_freq to avoid error message in codec */
ret = snd_soc_dai_set_sysclk(codec_dai,
codec_priv->mclk_id,
codec_priv->free_freq,
SND_SOC_CLOCK_IN);
if (ret) {
dev_err(dev, "failed to switch away from FLL: %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_pll(codec_dai,
codec_priv->pll_id, 0, 0, 0);
if (ret && ret != -ENOTSUPP) {
dev_err(dev, "failed to stop FLL: %d\n", ret);
return ret;
}
}
}
@ -504,9 +516,10 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
struct fsl_asoc_card_priv *priv = snd_soc_card_get_drvdata(card);
struct snd_soc_pcm_runtime *rtd = list_first_entry(
&card->rtd_list, struct snd_soc_pcm_runtime, list);
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
struct codec_priv *codec_priv = &priv->codec_priv;
struct snd_soc_dai *codec_dai;
struct codec_priv *codec_priv;
struct device *dev = card->dev;
int codec_idx;
int ret;
if (fsl_asoc_card_is_ac97(priv)) {
@ -526,32 +539,37 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
return 0;
}
ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
if (ret && ret != -ENOTSUPP) {
dev_err(dev, "failed to set sysclk in %s\n", __func__);
return ret;
}
for_each_rtd_codec_dais(rtd, codec_idx, codec_dai) {
codec_priv = &priv->codec_priv[codec_idx];
if (!IS_ERR_OR_NULL(codec_priv->mclk))
clk_prepare_enable(codec_priv->mclk);
ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
if (ret && ret != -ENOTSUPP) {
dev_err(dev, "failed to set sysclk in %s\n", __func__);
return ret;
}
if (!IS_ERR_OR_NULL(codec_priv->mclk))
clk_prepare_enable(codec_priv->mclk);
}
return 0;
}
static int fsl_asoc_card_probe(struct platform_device *pdev)
{
struct device_node *cpu_np, *codec_np, *asrc_np;
struct device_node *cpu_np, *asrc_np;
struct snd_soc_dai_link_component *codec_comp;
struct device_node *codec_np[2];
struct device_node *np = pdev->dev.of_node;
struct platform_device *asrc_pdev = NULL;
struct device_node *bitclkprovider = NULL;
struct device_node *frameprovider = NULL;
struct platform_device *cpu_pdev;
struct fsl_asoc_card_priv *priv;
struct device *codec_dev = NULL;
const char *codec_dai_name;
const char *codec_dev_name;
struct device *codec_dev[2] = { NULL, NULL };
const char *codec_dai_name[2];
const char *codec_dev_name[2];
u32 asrc_fmt = 0;
int codec_idx;
u32 width;
@ -580,21 +598,25 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
goto fail;
}
codec_np = of_parse_phandle(np, "audio-codec", 0);
if (codec_np) {
struct platform_device *codec_pdev;
struct i2c_client *codec_i2c;
codec_np[0] = of_parse_phandle(np, "audio-codec", 0);
codec_np[1] = of_parse_phandle(np, "audio-codec", 1);
codec_i2c = of_find_i2c_device_by_node(codec_np);
if (codec_i2c) {
codec_dev = &codec_i2c->dev;
codec_dev_name = codec_i2c->name;
}
if (!codec_dev) {
codec_pdev = of_find_device_by_node(codec_np);
if (codec_pdev) {
codec_dev = &codec_pdev->dev;
codec_dev_name = codec_pdev->name;
for (codec_idx = 0; codec_idx < 2; codec_idx++) {
if (codec_np[codec_idx]) {
struct platform_device *codec_pdev;
struct i2c_client *codec_i2c;
codec_i2c = of_find_i2c_device_by_node(codec_np[codec_idx]);
if (codec_i2c) {
codec_dev[codec_idx] = &codec_i2c->dev;
codec_dev_name[codec_idx] = codec_i2c->name;
}
if (!codec_dev[codec_idx]) {
codec_pdev = of_find_device_by_node(codec_np[codec_idx]);
if (codec_pdev) {
codec_dev[codec_idx] = &codec_pdev->dev;
codec_dev_name[codec_idx] = codec_pdev->name;
}
}
}
}
@ -604,12 +626,14 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
asrc_pdev = of_find_device_by_node(asrc_np);
/* Get the MCLK rate only, and leave it controlled by CODEC drivers */
if (codec_dev) {
struct clk *codec_clk = clk_get(codec_dev, NULL);
for (codec_idx = 0; codec_idx < 2; codec_idx++) {
if (codec_dev[codec_idx]) {
struct clk *codec_clk = clk_get(codec_dev[codec_idx], NULL);
if (!IS_ERR(codec_clk)) {
priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
clk_put(codec_clk);
if (!IS_ERR(codec_clk)) {
priv->codec_priv[codec_idx].mclk_freq = clk_get_rate(codec_clk);
clk_put(codec_clk);
}
}
}
@ -629,31 +653,33 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map);
priv->card.driver_name = DRIVER_NAME;
priv->codec_priv.fll_id = -1;
priv->codec_priv.pll_id = -1;
for (codec_idx = 0; codec_idx < 2; codec_idx++) {
priv->codec_priv[codec_idx].fll_id = -1;
priv->codec_priv[codec_idx].pll_id = -1;
}
/* Diversify the card configurations */
if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) {
codec_dai_name = "cs42888";
priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv.mclk_freq;
priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq;
codec_dai_name[0] = "cs42888";
priv->cpu_priv.sysclk_freq[TX] = priv->codec_priv[0].mclk_freq;
priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv[0].mclk_freq;
priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
priv->cpu_priv.slot_width = 32;
priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
} else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) {
codec_dai_name = "cs4271-hifi";
priv->codec_priv.mclk_id = CS427x_SYSCLK_MCLK;
codec_dai_name[0] = "cs4271-hifi";
priv->codec_priv[0].mclk_id = CS427x_SYSCLK_MCLK;
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
codec_dai_name = "sgtl5000";
priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
codec_dai_name[0] = "sgtl5000";
priv->codec_priv[0].mclk_id = SGTL5000_SYSCLK;
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) {
codec_dai_name = "tlv320aic32x4-hifi";
codec_dai_name[0] = "tlv320aic32x4-hifi";
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic31xx")) {
codec_dai_name = "tlv320dac31xx-hifi";
codec_dai_name[0] = "tlv320dac31xx-hifi";
priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
priv->dai_link[1].dpcm_capture = 0;
priv->dai_link[2].dpcm_capture = 0;
@ -662,23 +688,23 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->card.dapm_routes = audio_map_tx;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) {
codec_dai_name = "wm8962";
priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK;
priv->codec_priv.fll_id = WM8962_SYSCLK_FLL;
priv->codec_priv.pll_id = WM8962_FLL;
codec_dai_name[0] = "wm8962";
priv->codec_priv[0].mclk_id = WM8962_SYSCLK_MCLK;
priv->codec_priv[0].fll_id = WM8962_SYSCLK_FLL;
priv->codec_priv[0].pll_id = WM8962_FLL;
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8960")) {
codec_dai_name = "wm8960-hifi";
priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO;
priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO;
codec_dai_name[0] = "wm8960-hifi";
priv->codec_priv[0].fll_id = WM8960_SYSCLK_AUTO;
priv->codec_priv[0].pll_id = WM8960_SYSCLK_AUTO;
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) {
codec_dai_name = "ac97-hifi";
codec_dai_name[0] = "ac97-hifi";
priv->dai_fmt = SND_SOC_DAIFMT_AC97;
priv->card.dapm_routes = audio_map_ac97;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_ac97);
} else if (of_device_is_compatible(np, "fsl,imx-audio-mqs")) {
codec_dai_name = "fsl-mqs-dai";
codec_dai_name[0] = "fsl-mqs-dai";
priv->dai_fmt = SND_SOC_DAIFMT_LEFT_J |
SND_SOC_DAIFMT_CBC_CFC |
SND_SOC_DAIFMT_NB_NF;
@ -687,7 +713,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->card.dapm_routes = audio_map_tx;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8524")) {
codec_dai_name = "wm8524-hifi";
codec_dai_name[0] = "wm8524-hifi";
priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
priv->dai_link[1].dpcm_capture = 0;
priv->dai_link[2].dpcm_capture = 0;
@ -695,32 +721,32 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->card.dapm_routes = audio_map_tx;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
} else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) {
codec_dai_name = "si476x-codec";
codec_dai_name[0] = "si476x-codec";
priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
priv->card.dapm_routes = audio_map_rx;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx);
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8958")) {
codec_dai_name = "wm8994-aif1";
codec_dai_name[0] = "wm8994-aif1";
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
priv->codec_priv.mclk_id = WM8994_FLL_SRC_MCLK1;
priv->codec_priv.fll_id = WM8994_SYSCLK_FLL1;
priv->codec_priv.pll_id = WM8994_FLL1;
priv->codec_priv.free_freq = priv->codec_priv.mclk_freq;
priv->codec_priv[0].mclk_id = WM8994_FLL_SRC_MCLK1;
priv->codec_priv[0].fll_id = WM8994_SYSCLK_FLL1;
priv->codec_priv[0].pll_id = WM8994_FLL1;
priv->codec_priv[0].free_freq = priv->codec_priv[0].mclk_freq;
priv->card.dapm_routes = NULL;
priv->card.num_dapm_routes = 0;
} else if (of_device_is_compatible(np, "fsl,imx-audio-nau8822")) {
codec_dai_name = "nau8822-hifi";
priv->codec_priv.mclk_id = NAU8822_CLK_MCLK;
priv->codec_priv.fll_id = NAU8822_CLK_PLL;
priv->codec_priv.pll_id = NAU8822_CLK_PLL;
codec_dai_name[0] = "nau8822-hifi";
priv->codec_priv[0].mclk_id = NAU8822_CLK_MCLK;
priv->codec_priv[0].fll_id = NAU8822_CLK_PLL;
priv->codec_priv[0].pll_id = NAU8822_CLK_PLL;
priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
if (codec_dev)
priv->codec_priv.mclk = devm_clk_get(codec_dev, NULL);
if (codec_dev[0])
priv->codec_priv[0].mclk = devm_clk_get(codec_dev[0], NULL);
} else if (of_device_is_compatible(np, "fsl,imx-audio-wm8904")) {
codec_dai_name = "wm8904-hifi";
priv->codec_priv.mclk_id = WM8904_FLL_MCLK;
priv->codec_priv.fll_id = WM8904_CLK_FLL;
priv->codec_priv.pll_id = WM8904_FLL_MCLK;
codec_dai_name[0] = "wm8904-hifi";
priv->codec_priv[0].mclk_id = WM8904_FLL_MCLK;
priv->codec_priv[0].fll_id = WM8904_CLK_FLL;
priv->codec_priv[0].pll_id = WM8904_FLL_MCLK;
priv->dai_fmt |= SND_SOC_DAIFMT_CBP_CFP;
} else {
dev_err(&pdev->dev, "unknown Device Tree compatible\n");
@ -732,18 +758,30 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
* Allow setting mclk-id from the device-tree node. Otherwise, the
* default value for each card configuration is used.
*/
of_property_read_u32(np, "mclk-id", &priv->codec_priv.mclk_id);
for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) {
of_property_read_u32_index(np, "mclk-id", codec_idx,
&priv->codec_priv[codec_idx].mclk_id);
}
/* Format info from DT is optional. */
snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL, &bitclkprovider, &frameprovider);
if (bitclkprovider || frameprovider) {
unsigned int daifmt = snd_soc_daifmt_parse_format(np, NULL);
bool codec_bitclkprovider = false;
bool codec_frameprovider = false;
if (codec_np == bitclkprovider)
daifmt |= (codec_np == frameprovider) ?
for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) {
if (bitclkprovider && codec_np[codec_idx] == bitclkprovider)
codec_bitclkprovider = true;
if (frameprovider && codec_np[codec_idx] == frameprovider)
codec_frameprovider = true;
}
if (codec_bitclkprovider)
daifmt |= (codec_frameprovider) ?
SND_SOC_DAIFMT_CBP_CFP : SND_SOC_DAIFMT_CBP_CFC;
else
daifmt |= (codec_np == frameprovider) ?
daifmt |= (codec_frameprovider) ?
SND_SOC_DAIFMT_CBC_CFP : SND_SOC_DAIFMT_CBC_CFC;
/* Override dai_fmt with value from DT */
@ -759,7 +797,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
of_node_put(bitclkprovider);
of_node_put(frameprovider);
if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) {
if (!fsl_asoc_card_is_ac97(priv) && !codec_dev[0]) {
dev_dbg(&pdev->dev, "failed to find codec device\n");
ret = -EPROBE_DEFER;
goto asrc_fail;
@ -798,7 +836,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
ret = snd_soc_of_parse_card_name(&priv->card, "model");
if (ret) {
snprintf(priv->name, sizeof(priv->name), "%s-audio",
fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name);
fsl_asoc_card_is_ac97(priv) ? "ac97" : codec_dev_name[0]);
priv->card.name = priv->name;
}
priv->card.dai_link = priv->dai_link;
@ -820,11 +858,15 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
/* Normal DAI Link */
priv->dai_link[0].cpus->of_node = cpu_np;
priv->dai_link[0].codecs[0].dai_name = codec_dai_name;
for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) {
codec_comp->dai_name = codec_dai_name[codec_idx];
}
if (!fsl_asoc_card_is_ac97(priv))
priv->dai_link[0].codecs[0].of_node = codec_np;
else {
if (!fsl_asoc_card_is_ac97(priv)) {
for_each_link_codecs((&(priv->dai_link[0])), codec_idx, codec_comp) {
codec_comp->of_node = codec_np[codec_idx];
}
} else {
u32 idx;
ret = of_property_read_u32(cpu_np, "cell-index", &idx);
@ -926,7 +968,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
asrc_fail:
of_node_put(asrc_np);
of_node_put(codec_np);
of_node_put(codec_np[0]);
of_node_put(codec_np[1]);
put_device(&cpu_pdev->dev);
fail:
of_node_put(cpu_np);