From aa9c3b7273a58b5d9b2c1161b76b5fc8ea8c159b Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Thu, 13 Feb 2020 16:51:57 +0100 Subject: ASoC: meson: axg: extract sound card utils This prepares the addition of the GX SoC family sound card driver. The GX sound card, while slightly different, will be similar to the AXG one. The purpose of this change is to share the utils common to both sound card driver. Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20200213155159.3235792-8-jbrunet@baylibre.com Signed-off-by: Mark Brown --- sound/soc/meson/meson-card-utils.c | 385 +++++++++++++++++++++++++++++++++++++ 1 file changed, 385 insertions(+) create mode 100644 sound/soc/meson/meson-card-utils.c (limited to 'sound/soc/meson/meson-card-utils.c') diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c new file mode 100644 index 000000000000..a70d244ef88b --- /dev/null +++ b/sound/soc/meson/meson-card-utils.c @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2020 BayLibre, SAS. +// Author: Jerome Brunet + +#include +#include +#include + +#include "meson-card.h" + +int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + unsigned int mclk_fs) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai; + unsigned int mclk; + int ret, i; + + if (!mclk_fs) + return 0; + + mclk = params_rate(params) * mclk_fs; + + for_each_rtd_codec_dai(rtd, i, codec_dai) { + ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, + SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + return ret; + } + + ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, + SND_SOC_CLOCK_OUT); + if (ret && ret != -ENOTSUPP) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(meson_card_i2s_set_sysclk); + +int meson_card_reallocate_links(struct snd_soc_card *card, + unsigned int num_links) +{ + struct meson_card *priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai_link *links; + void **ldata; + + links = krealloc(priv->card.dai_link, + num_links * sizeof(*priv->card.dai_link), + GFP_KERNEL | __GFP_ZERO); + ldata = krealloc(priv->link_data, + num_links * sizeof(*priv->link_data), + GFP_KERNEL | __GFP_ZERO); + + if (!links || !ldata) { + dev_err(priv->card.dev, "failed to allocate links\n"); + return -ENOMEM; + } + + priv->card.dai_link = links; + priv->link_data = ldata; + priv->card.num_links = num_links; + return 0; +} +EXPORT_SYMBOL_GPL(meson_card_reallocate_links); + +int meson_card_parse_dai(struct snd_soc_card *card, + struct device_node *node, + struct device_node **dai_of_node, + const char **dai_name) +{ + struct of_phandle_args args; + int ret; + + if (!dai_name || !dai_of_node || !node) + return -EINVAL; + + ret = of_parse_phandle_with_args(node, "sound-dai", + "#sound-dai-cells", 0, &args); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(card->dev, "can't parse dai %d\n", ret); + return ret; + } + *dai_of_node = args.np; + + return snd_soc_get_dai_name(&args, dai_name); +} +EXPORT_SYMBOL_GPL(meson_card_parse_dai); + +static int meson_card_set_link_name(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node, + const char *prefix) +{ + char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", + prefix, node->full_name); + if (!name) + return -ENOMEM; + + link->name = name; + link->stream_name = name; + + return 0; +} + +unsigned int meson_card_parse_daifmt(struct device_node *node, + struct device_node *cpu_node) +{ + struct device_node *bitclkmaster = NULL; + struct device_node *framemaster = NULL; + unsigned int daifmt; + + daifmt = snd_soc_of_parse_daifmt(node, DT_PREFIX, + &bitclkmaster, &framemaster); + daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; + + /* If no master is provided, default to cpu master */ + if (!bitclkmaster || bitclkmaster == cpu_node) { + daifmt |= (!framemaster || framemaster == cpu_node) ? + SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM; + } else { + daifmt |= (!framemaster || framemaster == cpu_node) ? + SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM; + } + + of_node_put(bitclkmaster); + of_node_put(framemaster); + + return daifmt; +} +EXPORT_SYMBOL_GPL(meson_card_parse_daifmt); + +int meson_card_set_be_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node) +{ + struct snd_soc_dai_link_component *codec; + struct device_node *np; + int ret, num_codecs; + + link->no_pcm = 1; + link->dpcm_playback = 1; + link->dpcm_capture = 1; + + num_codecs = of_get_child_count(node); + if (!num_codecs) { + dev_err(card->dev, "be link %s has no codec\n", + node->full_name); + return -EINVAL; + } + + codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + link->codecs = codec; + link->num_codecs = num_codecs; + + for_each_child_of_node(node, np) { + ret = meson_card_parse_dai(card, np, &codec->of_node, + &codec->dai_name); + if (ret) { + of_node_put(np); + return ret; + } + + codec++; + } + + ret = meson_card_set_link_name(card, link, node, "be"); + if (ret) + dev_err(card->dev, "error setting %pOFn link name\n", np); + + return ret; +} +EXPORT_SYMBOL_GPL(meson_card_set_be_link); + +int meson_card_set_fe_link(struct snd_soc_card *card, + struct snd_soc_dai_link *link, + struct device_node *node, + bool is_playback) +{ + struct snd_soc_dai_link_component *codec; + + codec = devm_kzalloc(card->dev, sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + + link->codecs = codec; + link->num_codecs = 1; + + link->dynamic = 1; + link->dpcm_merged_format = 1; + link->dpcm_merged_chan = 1; + link->dpcm_merged_rate = 1; + link->codecs->dai_name = "snd-soc-dummy-dai"; + link->codecs->name = "snd-soc-dummy"; + + if (is_playback) + link->dpcm_playback = 1; + else + link->dpcm_capture = 1; + + return meson_card_set_link_name(card, link, node, "fe"); +} +EXPORT_SYMBOL_GPL(meson_card_set_fe_link); + +static int meson_card_add_links(struct snd_soc_card *card) +{ + struct meson_card *priv = snd_soc_card_get_drvdata(card); + struct device_node *node = card->dev->of_node; + struct device_node *np; + int num, i, ret; + + num = of_get_child_count(node); + if (!num) { + dev_err(card->dev, "card has no links\n"); + return -EINVAL; + } + + ret = meson_card_reallocate_links(card, num); + if (ret) + return ret; + + i = 0; + for_each_child_of_node(node, np) { + ret = priv->match_data->add_link(card, np, &i); + if (ret) { + of_node_put(np); + return ret; + } + + i++; + } + + return 0; +} + +static int meson_card_parse_of_optional(struct snd_soc_card *card, + const char *propname, + int (*func)(struct snd_soc_card *c, + const char *p)) +{ + /* If property is not provided, don't fail ... */ + if (!of_property_read_bool(card->dev->of_node, propname)) + return 0; + + /* ... but do fail if it is provided and the parsing fails */ + return func(card, propname); +} + +static int meson_card_add_aux_devices(struct snd_soc_card *card) +{ + struct device_node *node = card->dev->of_node; + struct snd_soc_aux_dev *aux; + int num, i; + + num = of_count_phandle_with_args(node, "audio-aux-devs", NULL); + if (num == -ENOENT) { + return 0; + } else if (num < 0) { + dev_err(card->dev, "error getting auxiliary devices: %d\n", + num); + return num; + } + + aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL); + if (!aux) + return -ENOMEM; + card->aux_dev = aux; + card->num_aux_devs = num; + + for_each_card_pre_auxs(card, i, aux) { + aux->dlc.of_node = + of_parse_phandle(node, "audio-aux-devs", i); + if (!aux->dlc.of_node) + return -EINVAL; + } + + return 0; +} + +static void meson_card_clean_references(struct meson_card *priv) +{ + struct snd_soc_card *card = &priv->card; + struct snd_soc_dai_link *link; + struct snd_soc_dai_link_component *codec; + struct snd_soc_aux_dev *aux; + int i, j; + + if (card->dai_link) { + for_each_card_prelinks(card, i, link) { + if (link->cpus) + of_node_put(link->cpus->of_node); + for_each_link_codecs(link, j, codec) + of_node_put(codec->of_node); + } + } + + if (card->aux_dev) { + for_each_card_pre_auxs(card, i, aux) + of_node_put(aux->dlc.of_node); + } + + kfree(card->dai_link); + kfree(priv->link_data); +} + +int meson_card_probe(struct platform_device *pdev) +{ + const struct meson_card_match_data *data; + struct device *dev = &pdev->dev; + struct meson_card *priv; + int ret; + + data = of_device_get_match_data(dev); + if (!data) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + snd_soc_card_set_drvdata(&priv->card, priv); + + priv->card.owner = THIS_MODULE; + priv->card.dev = dev; + priv->match_data = data; + + ret = snd_soc_of_parse_card_name(&priv->card, "model"); + if (ret < 0) + return ret; + + ret = meson_card_parse_of_optional(&priv->card, "audio-routing", + snd_soc_of_parse_audio_routing); + if (ret) { + dev_err(dev, "error while parsing routing\n"); + return ret; + } + + ret = meson_card_parse_of_optional(&priv->card, "audio-widgets", + snd_soc_of_parse_audio_simple_widgets); + if (ret) { + dev_err(dev, "error while parsing widgets\n"); + return ret; + } + + ret = meson_card_add_links(&priv->card); + if (ret) + goto out_err; + + ret = meson_card_add_aux_devices(&priv->card); + if (ret) + goto out_err; + + ret = devm_snd_soc_register_card(dev, &priv->card); + if (ret) + goto out_err; + + return 0; + +out_err: + meson_card_clean_references(priv); + return ret; +} +EXPORT_SYMBOL_GPL(meson_card_probe); + +int meson_card_remove(struct platform_device *pdev) +{ + struct meson_card *priv = platform_get_drvdata(pdev); + + meson_card_clean_references(priv); + + return 0; +} +EXPORT_SYMBOL_GPL(meson_card_remove); + +MODULE_DESCRIPTION("Amlogic Sound Card Utils"); +MODULE_AUTHOR("Jerome Brunet "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From b5c52f5801c6e076377f7f411fb61bab86cb9542 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 9 Mar 2020 13:07:48 +0900 Subject: ASoC: meson: use for_each_rtd_codecs/cpus_dai() macro This patch switch to use plural form macro. - for_each_rtd_codec_dai() + for_each_rtd_codec_dais() - for_each_rtd_codec_dai_rollback() + for_each_rtd_codec_dais_rollback() - for_each_rtd_cpu_dai() + for_each_rtd_cpu_dais() - for_each_rtd_cpu_dai_rollback() + for_each_rtd_cpu_dais_rollback() Signed-off-by: Kuninori Morimoto Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/87y2sagoh7.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/meson/axg-card.c | 2 +- sound/soc/meson/meson-card-utils.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/soc/meson/meson-card-utils.c') diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c index 48651631bdcf..77a7d5f36ebf 100644 --- a/sound/soc/meson/axg-card.c +++ b/sound/soc/meson/axg-card.c @@ -60,7 +60,7 @@ static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dai *codec_dai; int ret, i; - for_each_rtd_codec_dai(rtd, i, codec_dai) { + for_each_rtd_codec_dais(rtd, i, codec_dai) { ret = snd_soc_dai_set_tdm_slot(codec_dai, be->codec_masks[i].tx, be->codec_masks[i].rx, diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c index a70d244ef88b..b5d3c9f56bac 100644 --- a/sound/soc/meson/meson-card-utils.c +++ b/sound/soc/meson/meson-card-utils.c @@ -23,7 +23,7 @@ int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream, mclk = params_rate(params) * mclk_fs; - for_each_rtd_codec_dai(rtd, i, codec_dai) { + for_each_rtd_codec_dais(rtd, i, codec_dai) { ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN); if (ret && ret != -ENOTSUPP) -- cgit v1.2.3-70-g09d2 From 385a5c60ad7ac778a24c2715a2085241b2d6a7f4 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 23 Mar 2020 14:19:32 +0900 Subject: ASoC: meson: use asoc_rtd_to_cpu() / asoc_rtd_to_codec() macro for DAI pointer Signed-off-by: Kuninori Morimoto Tested-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/87mu87ir6j.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/meson/aiu-fifo.c | 2 +- sound/soc/meson/axg-card.c | 8 ++++---- sound/soc/meson/axg-fifo.c | 2 +- sound/soc/meson/meson-card-utils.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'sound/soc/meson/meson-card-utils.c') diff --git a/sound/soc/meson/aiu-fifo.c b/sound/soc/meson/aiu-fifo.c index da8c098e8750..d9cede4c33ff 100644 --- a/sound/soc/meson/aiu-fifo.c +++ b/sound/soc/meson/aiu-fifo.c @@ -26,7 +26,7 @@ static struct snd_soc_dai *aiu_fifo_dai(struct snd_pcm_substream *ss) { struct snd_soc_pcm_runtime *rtd = ss->private_data; - return rtd->cpu_dai; + return asoc_rtd_to_cpu(rtd, 0); } snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component, diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c index 77a7d5f36ebf..af46845f4ef2 100644 --- a/sound/soc/meson/axg-card.c +++ b/sound/soc/meson/axg-card.c @@ -72,10 +72,10 @@ static int axg_card_tdm_dai_init(struct snd_soc_pcm_runtime *rtd) } } - ret = axg_tdm_set_tdm_slots(rtd->cpu_dai, be->tx_mask, be->rx_mask, + ret = axg_tdm_set_tdm_slots(asoc_rtd_to_cpu(rtd, 0), be->tx_mask, be->rx_mask, be->slots, be->slot_width); if (ret) { - dev_err(rtd->cpu_dai->dev, "setting tdm link slots failed\n"); + dev_err(asoc_rtd_to_cpu(rtd, 0)->dev, "setting tdm link slots failed\n"); return ret; } @@ -90,10 +90,10 @@ static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd) int ret; /* The loopback rx_mask is the pad tx_mask */ - ret = axg_tdm_set_tdm_slots(rtd->cpu_dai, NULL, be->tx_mask, + ret = axg_tdm_set_tdm_slots(asoc_rtd_to_cpu(rtd, 0), NULL, be->tx_mask, be->slots, be->slot_width); if (ret) { - dev_err(rtd->cpu_dai->dev, "setting tdm link slots failed\n"); + dev_err(asoc_rtd_to_cpu(rtd, 0)->dev, "setting tdm link slots failed\n"); return ret; } diff --git a/sound/soc/meson/axg-fifo.c b/sound/soc/meson/axg-fifo.c index c12b0d5e8ebf..2e9b56b29d31 100644 --- a/sound/soc/meson/axg-fifo.c +++ b/sound/soc/meson/axg-fifo.c @@ -47,7 +47,7 @@ static struct snd_soc_dai *axg_fifo_dai(struct snd_pcm_substream *ss) { struct snd_soc_pcm_runtime *rtd = ss->private_data; - return rtd->cpu_dai; + return asoc_rtd_to_cpu(rtd, 0); } static struct axg_fifo *axg_fifo_data(struct snd_pcm_substream *ss) diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c index b5d3c9f56bac..2ca8c98e204f 100644 --- a/sound/soc/meson/meson-card-utils.c +++ b/sound/soc/meson/meson-card-utils.c @@ -30,7 +30,7 @@ int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream, return ret; } - ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, + ret = snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0), 0, mclk, SND_SOC_CLOCK_OUT); if (ret && ret != -ENOTSUPP) return ret; -- cgit v1.2.3-70-g09d2