diff mbox

[v3,2/2] ASoC: simple-card: add multi-CODECs in DT

Message ID 53ef077d2174f8eb5b861d6cebe7c9e732f06d52.1415096705.git.moinejf@free.fr (mailing list archive)
State New, archived
Headers show

Commit Message

Jean-Francois Moine Nov. 4, 2014, 10:21 a.m. UTC
This patch allows many CODECs per link to be defined in the device tree.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 .../devicetree/bindings/sound/simple-card.txt      |  28 ++---
 sound/soc/generic/simple-card.c                    | 135 +++++++++++++--------
 2 files changed, 94 insertions(+), 69 deletions(-)

Comments

Mark Brown Nov. 7, 2014, 12:30 p.m. UTC | #1
On Tue, Nov 04, 2014 at 11:21:46AM +0100, Jean-Francois Moine wrote:

This looks like it combines some refactoring to split out some of the
DAI node parsing code with some new code.  It's quite hard to read
what's going on here, separating the code motion and the new code would
make life a lot easier, I've stared at it for a bit and I'm still not
100% sure it's doing everything it's supposed to be.

> -Example 2 - many DAI links:
> +Example 2 - many DAI links and multi-CODECs:

This would be a lot easier to read if the change didn't completely
rewrite the example but just added the thing it wants to add an example
of; the example may be drawn from a real system but that's not the
point.

>  	} else {
> -		clk = of_clk_get(args.np, 0);
> +		clk = of_clk_get((struct device_node *) dai_np, 0);

Adding this cast looks suspicous - why?  As far as I can tell the
original code didn't need one.
Jean-Francois Moine Nov. 7, 2014, 7:05 p.m. UTC | #2
On Fri, 7 Nov 2014 12:30:35 +0000
Mark Brown <broonie@kernel.org> wrote:

> >  	} else {
> > -		clk = of_clk_get(args.np, 0);
> > +		clk = of_clk_get((struct device_node *) dai_np, 0);  
> 
> Adding this cast looks suspicous - why?  As far as I can tell the
> original code didn't need one.

Right, 'args.np' was (struct device_node *), but, now, 'dai_np' is
(const struct device_node *).

Changing 'dai_np' to (struct device_node *) in the
asoc_simple_card_sub_parse_of() argument asks for a cast in calling
this function because the field 'of_node' of the struct
snd_soc_dai_link_component is (const struct device_node *).

Do you better like this last cast?
Lars-Peter Clausen Nov. 7, 2014, 8:07 p.m. UTC | #3
On 11/07/2014 08:05 PM, Jean-Francois Moine wrote:
> On Fri, 7 Nov 2014 12:30:35 +0000
> Mark Brown <broonie@kernel.org> wrote:
>
>>>   	} else {
>>> -		clk = of_clk_get(args.np, 0);
>>> +		clk = of_clk_get((struct device_node *) dai_np, 0);
>>
>> Adding this cast looks suspicous - why?  As far as I can tell the
>> original code didn't need one.
>
> Right, 'args.np' was (struct device_node *), but, now, 'dai_np' is
> (const struct device_node *).
>
> Changing 'dai_np' to (struct device_node *) in the
> asoc_simple_card_sub_parse_of() argument asks for a cast in calling
> this function because the field 'of_node' of the struct
> snd_soc_dai_link_component is (const struct device_node *).
>
> Do you better like this last cast?

Casting const away is usually a sign that something is wrong and has the 
potential to result in undefined behavior. The correct fix in this case is 
probably to make the np argument for of_clk_get() const.

- Lars
Mark Brown Nov. 8, 2014, 9:48 a.m. UTC | #4
On Fri, Nov 07, 2014 at 08:05:57PM +0100, Jean-Francois Moine wrote:
> Mark Brown <broonie@kernel.org> wrote:

> > >  	} else {
> > > -		clk = of_clk_get(args.np, 0);
> > > +		clk = of_clk_get((struct device_node *) dai_np, 0);  

> > Adding this cast looks suspicous - why?  As far as I can tell the
> > original code didn't need one.

> Right, 'args.np' was (struct device_node *), but, now, 'dai_np' is
> (const struct device_node *).

> Changing 'dai_np' to (struct device_node *) in the
> asoc_simple_card_sub_parse_of() argument asks for a cast in calling
> this function because the field 'of_node' of the struct
> snd_soc_dai_link_component is (const struct device_node *).

> Do you better like this last cast?

No.  As Lars says having a cast in the first place is almost always a
sign that you're doing something wrong outside of passing things through
some of the generic callback interfaces.
Jean-Francois Moine Nov. 9, 2014, 6:42 p.m. UTC | #5
On Fri, 07 Nov 2014 21:07:03 +0100
Lars-Peter Clausen <lars@metafoo.de> wrote:

> Casting const away is usually a sign that something is wrong and has the 
> potential to result in undefined behavior. The correct fix in this case is 
> probably to make the np argument for of_clk_get() const.

Thanks. I am sending a patch for that.
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt
index c3cba60..6cb1816 100644
--- a/Documentation/devicetree/bindings/sound/simple-card.txt
+++ b/Documentation/devicetree/bindings/sound/simple-card.txt
@@ -65,7 +65,11 @@  properties should also be placed in the codec node if needed.
 
 Required CPU/CODEC subnodes properties:
 
-- sound-dai				: phandle and port of CPU/CODEC
+either
+  - sound-dai				: phandle and port of CPU/CODEC
+or
+  - sound-dais				: list of phandle and port of CODECs
+					  
 
 Optional CPU/CODEC subnodes properties:
 
@@ -119,37 +123,29 @@  sh_fsi2: sh_fsi2@ec230000 {
 	interrupts = <0 146 0x4>;
 };
 
-Example 2 - many DAI links:
+Example 2 - many DAI links and multi-CODECs:
 
 sound {
 	compatible = "simple-audio-card";
 	simple-audio-card,name = "Cubox Audio";
 
-	simple-audio-card,dai-link@0 {		/* I2S - HDMI */
+	simple-audio-card,dai-link@0 {		/* S/PDIF - HDMI & S/PDIF */
 		format = "i2s";
 		cpu {
-			sound-dai = <&audio1 0>;
-		};
-		codec {
-			sound-dai = <&tda998x 0>;
-		};
-	};
-
-	simple-audio-card,dai-link@1 {		/* S/PDIF - HDMI */
-		cpu {
 			sound-dai = <&audio1 1>;
 		};
 		codec {
-			sound-dai = <&tda998x 1>;
+			sound-dais = <&hdmi 0>, <&spdif_codec>;
 		};
 	};
 
-	simple-audio-card,dai-link@2 {		/* S/PDIF - S/PDIF */
+	simple-audio-card,dai-link@1 {		/* I2S - HDMI */
+		format = "i2s";
 		cpu {
-			sound-dai = <&audio1 1>;
+			sound-dai = <&audio1 0>;
 		};
 		codec {
-			sound-dai = <&spdif_codec>;
+			sound-dai = <&hdmi 1>;
 		};
 	};
 };
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index cd49d50..ed3b125 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -172,34 +172,12 @@  static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
 static int
 asoc_simple_card_sub_parse_of(struct device_node *np,
 			      struct asoc_simple_dai *dai,
-			      struct device_node **p_node,
-			      const char **name,
-			      int *args_count)
+			      const struct device_node *dai_np)
 {
-	struct of_phandle_args args;
 	struct clk *clk;
 	u32 val;
 	int ret;
 
-	/*
-	 * Get node via "sound-dai = <&phandle port>"
-	 * it will be used as xxx_of_node on soc_bind_dai_link()
-	 */
-	ret = of_parse_phandle_with_args(np, "sound-dai",
-					 "#sound-dai-cells", 0, &args);
-	if (ret)
-		return ret;
-
-	*p_node = args.np;
-
-	if (args_count)
-		*args_count = args.args_count;
-
-	/* Get dai->name */
-	ret = snd_soc_of_get_dai_name(np, name);
-	if (ret < 0)
-		return ret;
-
 	/* Parse TDM slot */
 	ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width);
 	if (ret)
@@ -222,7 +200,7 @@  asoc_simple_card_sub_parse_of(struct device_node *np,
 	} else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
 		dai->sysclk = val;
 	} else {
-		clk = of_clk_get(args.np, 0);
+		clk = of_clk_get((struct device_node *) dai_np, 0);
 		if (!IS_ERR(clk))
 			dai->sysclk = clk_get_rate(clk);
 	}
@@ -232,7 +210,6 @@  asoc_simple_card_sub_parse_of(struct device_node *np,
 
 static int asoc_simple_card_parse_daifmt(struct device_node *node,
 					 struct simple_card_data *priv,
-					 struct device_node *cpu,
 					 struct device_node *codec,
 					 char *prefix, int idx)
 {
@@ -285,62 +262,99 @@  static int asoc_simple_card_dai_link_of(struct device_node *node,
 	struct device *dev = simple_priv_to_dev(priv);
 	struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
 	struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
-	struct device_node *cpu = NULL;
-	struct device_node *codec = NULL;
+	struct device_node *np = NULL;
+	struct snd_soc_dai_link_component *component;
+	struct of_phandle_args args;
 	char *name;
 	char prop[128];
 	char *prefix = "";
-	int ret, cpu_args;
+	int ret, cpu_args, i, num_codecs;
 
 	/* For single DAI link & old style of DT node */
 	if (is_top_level_node)
 		prefix = "simple-audio-card,";
 
 	snprintf(prop, sizeof(prop), "%scpu", prefix);
-	cpu = of_get_child_by_name(node, prop);
-
-	snprintf(prop, sizeof(prop), "%scodec", prefix);
-	codec = of_get_child_by_name(node, prop);
-
-	if (!cpu || !codec) {
+	np = of_get_child_by_name(node, prop);
+	if (!np) {
 		ret = -EINVAL;
 		dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
 		goto dai_link_of_err;
 	}
 
-	ret = asoc_simple_card_parse_daifmt(node, priv,
-					    cpu, codec, prefix, idx);
-	if (ret < 0)
+	/* Get the CPU node and name */
+	ret = of_parse_phandle_with_args(np, "sound-dai",
+					 "#sound-dai-cells", 0, &args);
+	if (ret)
 		goto dai_link_of_err;
+	dai_link->cpu_of_node = args.np;
+	cpu_args = args.args_count;
 
-	ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai,
-					    &dai_link->cpu_of_node,
-					    &dai_link->cpu_dai_name,
-					    &cpu_args);
+	ret = snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name);
 	if (ret < 0)
 		goto dai_link_of_err;
 
-	ret = asoc_simple_card_sub_parse_of(codec, &dai_props->codec_dai,
-					    &dai_link->codec_of_node,
-					    &dai_link->codec_dai_name, NULL);
+	ret = asoc_simple_card_sub_parse_of(np, &dai_props->cpu_dai,
+					    dai_link->cpu_of_node);
 	if (ret < 0)
 		goto dai_link_of_err;
+	of_node_put(np);
 
-	if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
+	snprintf(prop, sizeof(prop), "%scodec", prefix);
+	np = of_get_child_by_name(node, prop);
+	if (!np) {
 		ret = -EINVAL;
+		dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
+		goto dai_link_of_err;
+	}
+
+	/* Get the node and name of the CODEC(s) */
+	name = "sound-dai";
+	num_codecs = of_count_phandle_with_args(np, name,
+						"#sound-dai-cells");
+	if (num_codecs <= 0) {
+		name = "sound-dais";
+		num_codecs = of_count_phandle_with_args(np, name,
+							"#sound-dai-cells");
+		if (num_codecs <= 0) {
+			ret = -EINVAL;
+			dev_err(dev, "No CODEC DAI phandle\n");
+			goto dai_link_of_err;
+		}
+	}
+	component = devm_kzalloc(dev,
+				 sizeof *component * num_codecs,
+				 GFP_KERNEL);
+	if (!component) {
+		ret = -ENOMEM;
 		goto dai_link_of_err;
 	}
+	dai_link->codecs = component;
+	dai_link->num_codecs = num_codecs;
+	ret = snd_soc_of_get_dai_link_codecs(np, name, dai_link);
+	if (ret < 0)
+		goto dai_link_of_err;
+
+	ret = asoc_simple_card_sub_parse_of(np, &dai_props->codec_dai,
+					    dai_link->codecs[0].of_node);
+	if (ret < 0)
+		goto dai_link_of_err;
 
 	/* Simple Card assumes platform == cpu */
 	dai_link->platform_of_node = dai_link->cpu_of_node;
 
+	ret = asoc_simple_card_parse_daifmt(node, priv,
+					    np, prefix, idx);
+	if (ret < 0)
+		goto dai_link_of_err;
+
 	/* DAI link name is created from CPU/CODEC dai name */
 	name = devm_kzalloc(dev,
 			    strlen(dai_link->cpu_dai_name)   +
-			    strlen(dai_link->codec_dai_name) + 2,
+			    strlen(dai_link->codecs[0].dai_name) + 2,
 			    GFP_KERNEL);
 	sprintf(name, "%s-%s", dai_link->cpu_dai_name,
-				dai_link->codec_dai_name);
+				dai_link->codecs[0].dai_name);
 	dai_link->name = dai_link->stream_name = name;
 	dai_link->ops = &asoc_simple_card_ops;
 	dai_link->init = asoc_simple_card_dai_init;
@@ -351,7 +365,7 @@  static int asoc_simple_card_dai_link_of(struct device_node *node,
 		dai_props->cpu_dai.fmt,
 		dai_props->cpu_dai.sysclk);
 	dev_dbg(dev, "\tcodec : %s / %04x / %d\n",
-		dai_link->codec_dai_name,
+		dai_link->codecs[0].dai_name,
 		dai_props->codec_dai.fmt,
 		dai_props->codec_dai.sysclk);
 
@@ -366,11 +380,20 @@  static int asoc_simple_card_dai_link_of(struct device_node *node,
 	 */
 	if (!cpu_args)
 		dai_link->cpu_dai_name = NULL;
+	goto out;
 
 dai_link_of_err:
-	of_node_put(cpu);
-	of_node_put(codec);
+	for (i = 0, component = dai_link->codecs;
+	     i < dai_link->num_codecs;
+	     i++, component++) {
+		if (!component->of_node)
+			break;
+		of_node_put((struct device_node *) component->of_node);
+		component->of_node = NULL;
+	}
 
+out:
+	of_node_put(np);
 	return ret;
 }
 
@@ -457,16 +480,22 @@  static int asoc_simple_card_unref(struct platform_device *pdev)
 {
 	struct snd_soc_card *card = platform_get_drvdata(pdev);
 	struct snd_soc_dai_link *dai_link;
+	struct snd_soc_dai_link_component *component;
 	struct device_node *np;
-	int num_links;
+	int num_links, i;
 
 	for (num_links = 0, dai_link = card->dai_link;
 	     num_links < card->num_links;
 	     num_links++, dai_link++) {
 		np = (struct device_node *) dai_link->cpu_of_node;
 		of_node_put(np);
-		np = (struct device_node *) dai_link->codec_of_node;
-		of_node_put(np);
+		for (i = 0, component = dai_link->codecs;
+		     i < dai_link->num_codecs;
+		     i++, component++) {
+			if (!component->of_node)
+				break;
+			of_node_put((struct device_node *) component->of_node);
+		}
 	}
 	return 0;
 }