From c46cf13eae4a85b3ace766f2e097b0d6a99cee1a Mon Sep 17 00:00:00 2001
From: Adrian Alonso <adrian.alonso@nxp.com>
Date: Fri, 25 Mar 2022 17:51:24 -0500
Subject: [PATCH] LF-5093-1: sound: soc: fsl: imx: pcm512x: use asoc parse dt

Use simple card utils and imx asoc utils to parse
audio card device tree to handle when multiple codecs
are present on audio hats.

Signed-off-by: Adrian Alonso <adrian.alonso@nxp.com>
Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
 sound/soc/fsl/imx-pcm512x.c | 427 ++++++++++++++++++++----------------
 1 file changed, 239 insertions(+), 188 deletions(-)

diff --git a/sound/soc/fsl/imx-pcm512x.c b/sound/soc/fsl/imx-pcm512x.c
index 323dbff8be654..e3914533b0484 100644
--- a/sound/soc/fsl/imx-pcm512x.c
+++ b/sound/soc/fsl/imx-pcm512x.c
@@ -15,12 +15,12 @@
 #include <linux/slab.h>
 #include <linux/gpio/consumer.h>
 #include <linux/of_device.h>
-#include <linux/i2c.h>
 #include <linux/of_gpio.h>
 #include <linux/clk.h>
 #include <sound/soc.h>
 #include <sound/pcm_params.h>
 #include <sound/pcm.h>
+#include <sound/simple_card.h>
 #include <sound/soc-dapm.h>
 
 #include "fsl_sai.h"
@@ -30,14 +30,8 @@
 #define DAC_CLK_EXT_48K 24576000UL
 
 struct imx_pcm512x_data {
-	struct snd_soc_dai_link dai_link[1];
-	struct snd_soc_card card;
-	struct snd_soc_codec_conf *codec_conf;
+	struct asoc_simple_priv *priv;
 	struct gpio_desc *mute_gpio;
-	int num_codec_conf;
-	unsigned int slots;
-	unsigned int slot_width;
-	unsigned int daifmt;
 	bool dac_sclk;
 	bool dac_pluspro;
 	bool dac_led_status;
@@ -122,7 +116,7 @@ static int imx_pcm512x_dai_init(struct snd_soc_pcm_runtime *rtd)
 	if (data->dac_gain_limit) {
 		ret = snd_soc_limit_volume(card, "Digital Playback Volume", 207);
 		if (ret)
-			dev_warn(card->dev, "fail to set volume limit");
+			dev_warn(card->dev, "failed to set volume limit\n");
 	}
 
 	return 0;
@@ -163,20 +157,21 @@ static int imx_pcm512x_set_bias_level(struct snd_soc_card *card,
 static unsigned long pcm512x_get_mclk_rate(struct snd_pcm_substream *substream,
 					  struct snd_pcm_hw_params *params)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 	struct imx_pcm512x_data *data = snd_soc_card_get_drvdata(rtd->card);
-	unsigned int width = data->slots * data->slot_width;
+	unsigned int channels = params_channels(params);
+	unsigned int width = params_width(params);
 	unsigned int rate = params_rate(params);
+	unsigned int ratio = channels * width;
 	int i;
 
 	for (i = 0; i < ARRAY_SIZE(fs_map); i++) {
 		if (rate >= fs_map[i].rmin && rate <= fs_map[i].rmax) {
-			width = max(width, fs_map[i].wmin);
-			width = min(width, fs_map[i].wmax);
+			ratio = max(ratio, fs_map[i].wmin);
+			ratio = min(ratio, fs_map[i].wmax);
 			/* Adjust SAI bclk:mclk ratio */
-			width *= data->one2one_ratio ? 1 : 2;
-
-			return rate * width;
+			ratio *= data->one2one_ratio ? 1 : 2;
+			return rate * ratio;
 		}
 	}
 
@@ -187,22 +182,21 @@ static unsigned long pcm512x_get_mclk_rate(struct snd_pcm_substream *substream,
 static int imx_pcm512x_hw_params(struct snd_pcm_substream *substream,
 				struct snd_pcm_hw_params *params)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
 	struct snd_soc_component *comp = codec_dai->component;
 	struct snd_soc_card *card = rtd->card;
 	struct imx_pcm512x_data *data = snd_soc_card_get_drvdata(card);
-	unsigned int sample_rate = params_rate(params);
+	unsigned int rate = params_rate(params);
+	unsigned int channels = params_channels(params);
+	unsigned int width = params_width(params);
 	unsigned long mclk_freq;
 	int ret, i;
 
-	data->slots = 2;
-	data->slot_width = params_physical_width(params);
-
 	/* set MCLK freq */
 	if (data->dac_pluspro && data->dac_sclk) {
-		if (do_div(sample_rate, 8000)) {
+		if (do_div(rate, 8000)) {
 			mclk_freq = DAC_CLK_EXT_44K;
 			imx_pcm512x_select_ext_clk(comp, DAC_CLK_EXT_44EN);
 			ret = snd_soc_dai_set_sysclk(codec_dai,
@@ -225,46 +219,24 @@ static int imx_pcm512x_hw_params(struct snd_pcm_substream *substream,
 				mclk_freq, ret);
 	}
 
-	ret = snd_soc_dai_set_fmt(cpu_dai, data->daifmt);
-	if (ret) {
-		dev_err(card->dev, "failed to set cpu dai fmt: %d\n", ret);
-		return ret;
-	}
-
-	ret = snd_soc_dai_set_bclk_ratio(cpu_dai, data->slots * data->slot_width);
-	if (ret) {
-		dev_err(card->dev, "failed to set cpu dai bclk ratio\n");
-		return ret;
-	}
-
-	for (i = 0; i < rtd->num_codecs; i++) {
-		struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, i);
-
-		ret = snd_soc_dai_set_fmt(codec_dai, data->daifmt);
-		if (ret) {
-			dev_err(card->dev, "failed to set codec dai[%d] fmt: %d\n",
-					i, ret);
-			return ret;
-		}
-
-		ret = snd_soc_dai_set_bclk_ratio(codec_dai,
-					data->slots * data->slot_width);
+	for_each_rtd_codec_dais(rtd, i, codec_dai) {
+		ret = snd_soc_dai_set_bclk_ratio(codec_dai, (channels * width));
 		if (ret) {
-			dev_err(card->dev, "failed to set cpu dai bclk ratio\n");
+			dev_err(card->dev, "failed to set codec dai bclk ratio\n");
 			return ret;
 		}
 	}
 
-	dev_dbg(card->dev, "mclk_freq: %lu; bclk_ratio: %d\n", mclk_freq,
-		data->slots * data->slot_width);
+	dev_dbg(card->dev, "mclk_freq: %lu, bclk ratio: %u\n",
+		mclk_freq, (channels * width));
 
-	return ret;
+	return 0;
 }
 
 static int imx_pcm512x_startup(struct snd_pcm_substream *substream)
 {
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_card *card = rtd->card;
 	struct imx_pcm512x_data *data = snd_soc_card_get_drvdata(card);
 	static struct snd_pcm_hw_constraint_list constraint_rates;
@@ -328,180 +300,259 @@ static struct snd_soc_ops imx_pcm512x_ops = {
 	.shutdown = imx_pcm512x_shutdown,
 };
 
-SND_SOC_DAILINK_DEFS(hifi,
-	DAILINK_COMP_ARRAY(COMP_EMPTY()),
-	DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "pcm512x-hifi")),
-	DAILINK_COMP_ARRAY(COMP_EMPTY()));
-
-static struct snd_soc_dai_link_component pcm512x_codecs[] = {
-	{
-		/* Playback */
-		.dai_name = "pcm512x-hifi",
-	},
-};
-
-static struct snd_soc_dai_link imx_pcm512x_dai[] = {
-	{
-		.name = "pcm512x-audio",
-		.stream_name = "audio",
-		.codecs = pcm512x_codecs,
-		.num_codecs = 1,
-		.ops = &imx_pcm512x_ops,
-		.init = imx_pcm512x_dai_init,
-		.ignore_pmdown_time = 1,
-		SND_SOC_DAILINK_REG(hifi),
-	},
-};
-
-static int imx_pcm512x_probe(struct platform_device *pdev)
+static int imx_asoc_card_parse_dt(struct snd_soc_card *card,
+				  struct asoc_simple_priv *priv)
 {
-	struct device_node *bitclkmaster, *framemaster = NULL;
-	struct device_node *cpu_np, *codec_np = NULL;
-	struct device_node *np = pdev->dev.of_node;
-	struct platform_device *cpu_pdev = NULL;
-	struct snd_soc_dai_link_component *comp;
-	struct imx_pcm512x_data *data;
-	struct i2c_client *codec_dev;
-	int ret;
-
-	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
+	struct device_node *np, *cpu_np, *codec_np = NULL;
+	struct snd_soc_dai_link_component *dlc;
+	struct asoc_simple_dai *simple_dai;
+	struct device *dev = card->dev;
+	struct snd_soc_dai_link *link;
+	struct of_phandle_args args;
+	int ret, num_links;
+
+	ret = snd_soc_of_parse_card_name(card, "model");
+	if (ret) {
+		dev_err(dev, "failed to find card model name\n");
+		return ret;
+	}
 
-	comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL);
-	if (!comp)
-		return -ENOMEM;
+	if (of_property_read_bool(dev->of_node, "audio-routing")) {
+		ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+		if (ret) {
+			dev_err(dev, "failed to parse audio-routing\n");
+			return ret;
+		}
+	}
 
-	cpu_np = of_parse_phandle(np, "audio-cpu", 0);
-	if (!cpu_np) {
-		dev_err(&pdev->dev, "audio dai phandle missing or invalid\n");
-		ret = -EINVAL;
-		goto fail;
+	if (of_property_read_bool(dev->of_node, "audio-widgets")) {
+		ret = snd_soc_of_parse_audio_simple_widgets(card,
+							    "audio-widgets");
+		if (ret) {
+			dev_err(dev, "failed to parse audio-widgets\n");
+			return ret;
+		}
 	}
 
-	codec_np = of_parse_phandle(np, "audio-codec", 0);
-	if (!codec_np) {
-		dev_err(&pdev->dev, "audio codec phandle missing or invalid\n");
-		ret = -EINVAL;
-		goto fail;
+	if (of_property_read_bool(dev->of_node, "aux-devs")) {
+		ret = snd_soc_of_parse_aux_devs(card, "aux-devs");
+		if (ret) {
+			dev_err(dev, "failed to parse aux devs\n");
+			return ret;
+		}
 	}
 
-	cpu_pdev = of_find_device_by_node(cpu_np);
-	if (!cpu_pdev) {
-		dev_err(&pdev->dev, "failed to find SAI platform device\n");
-		ret = -EINVAL;
-		goto fail;
+	num_links = of_get_child_count(dev->of_node);
+
+	card->dai_link = devm_kcalloc(dev, num_links, sizeof(*link), GFP_KERNEL);
+	if (!card->dai_link) {
+		dev_err(dev, "failed to allocate memory for dai_link\n");
+		return -ENOMEM;
 	}
 
-	codec_dev = of_find_i2c_device_by_node(codec_np);
-	if (!codec_dev || !codec_dev->dev.driver) {
-		dev_err(&pdev->dev, "failed to find codec device\n");
-		ret = -EPROBE_DEFER;
-		goto fail;
+	simple_dai = devm_kcalloc(dev, num_links, sizeof(*simple_dai), GFP_KERNEL);
+	if (!simple_dai) {
+		dev_err(dev, "failed to allocate memory for simple_dai\n");
+		return -ENOMEM;
 	}
 
-	data->dac_gain_limit = of_property_read_bool(np, "dac,24db_digital_gain");
-	data->dac_auto_mute = of_property_read_bool(np, "dac,auto_mute_amp");
-	data->dac_gpio_unmute = of_property_read_bool(np, "dac,unmute_amp");
-	data->dac_led_status = of_property_read_bool(np, "dac,led_status");
-	data->one2one_ratio = true;
+	link = card->dai_link;
+	card->num_links = num_links;
+	/* pupulate dai links */
+	for_each_child_of_node(dev->of_node, np) {
+		dlc = devm_kzalloc(dev, 2 * sizeof(*dlc), GFP_KERNEL);
+		if (!dlc) {
+			dev_err(dev, "failed to allocate memory for dlc\n");
+			ret = -ENOMEM;
+			goto err_fail;
+		}
 
-	if (data->dac_auto_mute || data->dac_gpio_unmute) {
-		data->mute_gpio = devm_gpiod_get_optional(&pdev->dev,
-						"mute-amp", GPIOD_OUT_LOW);
-		if (IS_ERR(data->mute_gpio)) {
-			dev_err(&pdev->dev, "failed to get mute amp gpio\n");
-			ret = PTR_ERR(data->mute_gpio);
+		link->cpus = &dlc[0];
+		link->platforms = &dlc[1];
+		link->num_cpus = 1;
+		link->num_platforms = 1;
+
+		if (of_property_read_bool(np, "link-name")) {
+			ret = of_property_read_string(np, "link-name", &link->name);
+			if (ret) {
+				dev_err(dev, "failed to get dai_link name\n");
+				goto fail;
+			}
+		}
+
+		cpu_np = of_get_child_by_name(np, "cpu");
+		if (!cpu_np) {
+			dev_err(dev, "failed to get cpu phandle missing or invalid");
+			ret = -EINVAL;
 			goto fail;
 		}
-	}
 
-	if (data->dac_auto_mute && data->dac_gpio_unmute)
-		data->card.set_bias_level = imx_pcm512x_set_bias_level;
+		ret = of_parse_phandle_with_args(cpu_np, "sound-dai",
+						 "#sound-dai-cells", 0, &args);
+		if (ret) {
+			dev_err(dev, "failed to get cpu sound-dais\n");
+			goto fail;
+		}
 
-	platform_set_drvdata(pdev, &data->card);
-	snd_soc_card_set_drvdata(&data->card, data);
+		ret = snd_soc_of_get_dai_name(cpu_np, &link->cpus->dai_name);
+		if (ret) {
+			if (ret != -EPROBE_DEFER)
+				dev_err(dev, "failed to get cpu dai name\n");
+			goto fail;
+		}
 
-	memcpy(data->dai_link, imx_pcm512x_dai,
-	       sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(data->dai_link));
+		link->cpus->of_node = args.np;
+		link->id = args.args[0];
+		link->platforms->of_node = link->cpus->of_node;
 
-	data->card.owner = THIS_MODULE;
-	data->card.dev = &pdev->dev;
-	data->card.dai_link = data->dai_link;
-	data->card.num_links = 1;
+		codec_np = of_get_child_by_name(np, "codec");
+		if (!codec_np) {
+			dev_err(dev, "failed to get codec phandle missing or invalid\n");
+			goto fail;
+		}
 
-	ret = snd_soc_of_parse_card_name(&data->card, "model");
-	if (ret) {
-		dev_err(&pdev->dev, "failed to find card model name\n");
-		goto fail;
-	}
+		if (codec_np) {
+			ret = snd_soc_of_get_dai_link_codecs(dev, codec_np, link);
+			if (ret) {
+				if (ret != -EPROBE_DEFER)
+					dev_err(dev, "failed to get codec dais\n");
+				goto fail;
+			}
+		} else {
+			dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL);
+			if (!dlc) {
+				dev_err(dev, "failed to allocate memory for dlc\n");
+				ret = -ENOMEM;
+				goto err_fail;
+			}
+
+			link->codecs = dlc;
+			link->num_codecs = 1;
+
+			link->codecs->dai_name = "snd-soc-dummy-dai";
+			link->codecs->name = "snd-soc-dummy";
+		}
 
-	if (of_property_read_bool(np, "audio-routing")) {
-		ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
+		ret = asoc_simple_parse_daifmt(dev, np, codec_np, NULL,
+					       &link->dai_fmt);
 		if (ret) {
-			dev_err(&pdev->dev, "failed to parse audio-routing\n");
-			goto fail;
+			dev_warn(dev, "failed to parse dai format\n");
+			link->dai_fmt = SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBS_CFS |
+				SND_SOC_DAIFMT_I2S;
 		}
-	}
 
-	if (of_property_read_bool(np, "audio-widgets")) {
-		ret = snd_soc_of_parse_audio_simple_widgets(&data->card, "audio-widgets");
+		ret = asoc_simple_parse_tdm(np, simple_dai);
 		if (ret) {
-			dev_err(&pdev->dev, "failed to parse audio-widgets\n");
-			goto fail;
+			dev_err(dev, "failed to parse dai tdm\n");
 		}
-	}
 
-	snd_soc_daifmt_parse_clock_provider_as_phandle(np, NULL, &bitclkmaster, &framemaster);
-	data->daifmt = snd_soc_daifmt_parse_format(np, NULL);
-
-	if (codec_np == bitclkmaster)
-		data->daifmt |= (codec_np == framemaster) ?
-			SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
-	else
-		data->daifmt |= (codec_np == framemaster) ?
-			SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
-
-	if (!bitclkmaster)
-		of_node_put(bitclkmaster);
-	if (!framemaster)
-		of_node_put(framemaster);
-
-	if (of_property_read_bool(codec_np, "clocks"))
-		data->dac_sclk = true;
-
-	data->dai_link[0].cpus = &comp[0];
-	data->dai_link[0].num_cpus = 1;
-	data->dai_link[0].codecs = &comp[1];
-	data->dai_link[0].num_codecs = 1;
-	data->dai_link[0].platforms = &comp[2];
-	data->dai_link[0].num_platforms = 1;
-
-	data->dai_link[0].cpus->of_node = cpu_np;
-	data->dai_link[0].platforms->of_node = cpu_np;
-	data->dai_link[0].codecs->of_node = codec_np;
-	data->dai_link[0].codecs->dai_name = "pcm512x-hifi";
-	data->dai_link[0].dai_fmt = data->daifmt;
-
-	ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
-	if (ret) {
-		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
-		goto fail;
+		link->stream_name = link->name;
+		link->ignore_pmdown_time = 1;
+		simple_dai++;
+		link++;
+
+		of_node_put(cpu_np);
+		of_node_put(codec_np);
 	}
 
-	if (data->dac_gpio_unmute && data->dac_auto_mute)
-		gpiod_set_value_cansleep(data->mute_gpio, 1);
+	return 0;
 
-	ret = 0;
 fail:
 	if (cpu_np)
 		of_node_put(cpu_np);
 	if (codec_np)
 		of_node_put(codec_np);
+err_fail:
+	if (np)
+		of_node_put(np);
 
 	return ret;
 }
 
+static int imx_pcm512x_parse_dt(struct imx_pcm512x_data *data)
+{
+	struct snd_soc_card *card = &data->priv->snd_card;
+	struct device *dev = card->dev;
+	struct device_node *np = dev->of_node;
+	int ret;
+
+	/* Multiple dais */
+	ret = imx_asoc_card_parse_dt(card, data->priv);
+	if (ret)
+		return ret;
+
+	data->dac_gain_limit =
+		of_property_read_bool(np, "dac,24db_digital_gain");
+	data->dac_auto_mute =
+		of_property_read_bool(np, "dac,auto_mute_amp");
+	data->dac_gpio_unmute =
+		of_property_read_bool(np, "dac,unmute_amp");
+	data->dac_led_status =
+		of_property_read_bool(np, "dac,led_status");
+	data->dac_sclk =
+		of_property_read_bool(np, "dac,sclk");
+
+	return 0;
+}
+
+static int imx_pcm512x_probe(struct platform_device *pdev)
+{
+	struct asoc_simple_priv *priv;
+	struct imx_pcm512x_data *data;
+	struct snd_soc_card *card;
+	int ret, i;
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!data->priv)
+		return -ENOMEM;
+
+	card = &data->priv->snd_card;
+	dev_set_drvdata(&pdev->dev, &data);
+	snd_soc_card_set_drvdata(card, data);
+
+	card->owner = THIS_MODULE;
+	card->dev = &pdev->dev;
+
+	ret = imx_pcm512x_parse_dt(data);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to parse sound card dts\n");
+		return ret;
+	}
+
+	for (i = 0; i < card->num_links; i++) {
+		card->dai_link->ops = &imx_pcm512x_ops;
+		card->dai_link->init = &imx_pcm512x_dai_init;
+	}
+
+	if (data->dac_auto_mute || data->dac_gpio_unmute) {
+		data->mute_gpio = devm_gpiod_get_optional(&pdev->dev,
+						"mute-amp", GPIOD_OUT_LOW);
+		if (IS_ERR(data->mute_gpio)) {
+			dev_err(&pdev->dev, "failed to get mute amp gpio\n");
+			return PTR_ERR(data->mute_gpio);
+		}
+	}
+
+	if (data->dac_auto_mute && data->dac_gpio_unmute)
+		card->set_bias_level = imx_pcm512x_set_bias_level;
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register snd card\n");
+		return ret;
+	}
+
+	if (data->dac_gpio_unmute && data->dac_auto_mute)
+		gpiod_set_value_cansleep(data->mute_gpio, 1);
+
+	return 0;
+}
+
 static int imx_pcm512x_remove(struct platform_device *pdev)
 {
 	struct imx_pcm512x_data *data = platform_get_drvdata(pdev);
@@ -509,7 +560,7 @@ static int imx_pcm512x_remove(struct platform_device *pdev)
 	if (data->mute_gpio)
 		gpiod_set_value_cansleep(data->mute_gpio, 0);
 
-	return snd_soc_unregister_card(&data->card);
+	return snd_soc_unregister_card(&data->priv->snd_card);
 }
 
 static const struct of_device_id imx_pcm512x_dt_ids[] = {
-- 
GitLab