[PATCH v2 00/15] ASoC: add multi Component support
Hi Mark
These are v2 of multi Component support.
On below HW case, we would like to use it as "2 Cards", but unfortunately it is impossible in intuitive way, or possible but not intuitive way. In reality, it is handled as "1 big Card" today.
+-- basic board --------+ |+--------+ | || CPU ch0| <--> CodecA | || ch1| <-+ | |+--------+ | | +-------------|---------+ +-- expansion board ----+ | | | | +-> CodecB| +-----------------------+
To handling it as intuitive "2 Cards", this patch-set adds multi Component support.
To enable this patch-set, I included [01/15] patch into this patch-set which is posted but not yet accepted.
v1 -> v2 - include [01/15] patch into this patch-set - add related member to To - add DT member to [14/15][15/15]
Link: https://lore.kernel.org/all/87a6b6cofh.wl-kuninori.morimoto.gx@renesas.com/ Link: https://lore.kernel.org/r/87r0q5blta.wl-kuninori.morimoto.gx@renesas.com Link: https://lore.kernel.org/r/20230623-asoc-fix-meson-probe-v1-1-82b2c2ec5ca4@ke...
Kuninori Morimoto (15): ASoC: soc-core: protect dlc->of_node under mutex ASoC: soc-core.c: initialize dlc on snd_soc_get_dai_id() ASoC: soc-core.c: cleanup soc_dai_link_sanity_check() ASoC: soc-dai.c: add DAI get/match functions ASoC: soc-core.c: enable multi Component ASoC: soc-core.c: add snd_soc_get_dai_via_args() ASoC: soc-core.c: add snd_soc_dlc_use_cpu_as_platform() ASoC: soc-core.c: add snd_soc_copy_dai_args() ASoC: simple-card-utils.c: enable multi Component support ASoC: simple-card.c: enable multi Component support ASoC: rsnd: use DAI driver ID instead of DAI ID ASoC: rsnd: cleanup rsnd_dai_of_node() ASoC: rsnd: enable multi Component support for Audio Graph Card/Card2 ASoC: dt-bindings: renesas,rsnd.yaml: add common port-def ASoC: dt-bindings: renesas,rsnd.yaml: enable multi ports for multi Component support
.../bindings/sound/renesas,rsnd.yaml | 60 ++-- include/sound/simple_card_utils.h | 5 +- include/sound/soc-dai.h | 3 + include/sound/soc.h | 6 + sound/soc/generic/audio-graph-card.c | 2 +- sound/soc/generic/audio-graph-card2.c | 2 +- sound/soc/generic/simple-card-utils.c | 23 +- sound/soc/generic/simple-card.c | 20 +- sound/soc/sh/rcar/core.c | 146 +++++++--- sound/soc/sh/rcar/rsnd.h | 4 + sound/soc/soc-core.c | 273 +++++++++++++----- 11 files changed, 378 insertions(+), 166 deletions(-)
dlc->of_node will be set on snd_soc_get_dlc(), but we want 1) protect it by mutex, 2) set only when successed. This patch do it.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/soc-core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1a0bde23f5e6..51791f8b2af3 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3264,8 +3264,6 @@ int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_ struct snd_soc_component *pos; int ret = -EPROBE_DEFER;
- dlc->of_node = args->np; - mutex_lock(&client_mutex); for_each_component(pos) { struct device_node *component_of_node = soc_component_to_node(pos); @@ -3319,6 +3317,10 @@ int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_
break; } + + if (ret == 0) + dlc->of_node = args->np; + mutex_unlock(&client_mutex); return ret; }
Current snd_soc_get_dai_id() is initializing dlc *manually*, but it will might be a problem if dlc had new extra parameter. This patch uses default initialization, otherwise, non initialized part will be strange value. This is prepare for multi Component support.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/soc-core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 51791f8b2af3..47a5692209e8 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3235,11 +3235,12 @@ EXPORT_SYMBOL_GPL(snd_soc_get_stream_cpu); int snd_soc_get_dai_id(struct device_node *ep) { struct snd_soc_component *component; - struct snd_soc_dai_link_component dlc; + struct snd_soc_dai_link_component dlc = { + .of_node = of_graph_get_port_parent(ep), + }; int ret;
- dlc.of_node = of_graph_get_port_parent(ep); - dlc.name = NULL; + /* * For example HDMI case, HDMI has video/sound port, * but ALSA SoC needs sound port number only.
Required CPU/Codec/Platform dlc (snd_soc_dai_link_component) are similar but not same, and very complex. Current implementation is very confusable and it will be more complex if multi Component was supported. This patch cleanup it.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/soc-core.c | 131 ++++++++++++++++++++++++------------------- 1 file changed, 72 insertions(+), 59 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 47a5692209e8..94e856d67a46 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -238,6 +238,21 @@ static inline void snd_soc_debugfs_exit(void) { }
#endif
+static inline int snd_soc_dlc_component_is_empty(struct snd_soc_dai_link_component *dlc) +{ + return !(dlc->name || dlc->of_node); +} + +static inline int snd_soc_dlc_component_is_invalid(struct snd_soc_dai_link_component *dlc) +{ + return (dlc->name && dlc->of_node); +} + +static inline int snd_soc_dlc_dai_is_empty(struct snd_soc_dai_link_component *dlc) +{ + return !dlc->dai_name; +} + static int snd_soc_rtd_add_component(struct snd_soc_pcm_runtime *rtd, struct snd_soc_component *component) { @@ -829,102 +844,100 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, struct snd_soc_dai_link *link) { int i; - struct snd_soc_dai_link_component *cpu, *codec, *platform; + struct snd_soc_dai_link_component *dlc;
- for_each_link_codecs(link, i, codec) { + /* Codec check */ + for_each_link_codecs(link, i, dlc) { /* * Codec must be specified by 1 of name or OF node, * not both or neither. */ - if (!!codec->name == !!codec->of_node) { - dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n", - link->name); - return -EINVAL; - } + if (snd_soc_dlc_component_is_invalid(dlc)) + goto component_invalid; + + if (snd_soc_dlc_component_is_empty(dlc)) + goto component_empty;
/* Codec DAI name must be specified */ - if (!codec->dai_name) { - dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n", - link->name); - return -EINVAL; - } + if (snd_soc_dlc_dai_is_empty(dlc)) + goto dai_empty;
/* * Defer card registration if codec component is not added to * component list. */ - if (!soc_find_component(codec)) { - dev_dbg(card->dev, - "ASoC: codec component %s not found for link %s\n", - codec->name, link->name); - return -EPROBE_DEFER; - } + if (!soc_find_component(dlc)) + goto component_not_find; }
- for_each_link_platforms(link, i, platform) { + /* Platform check */ + for_each_link_platforms(link, i, dlc) { /* * Platform may be specified by either name or OF node, but it * can be left unspecified, then no components will be inserted * in the rtdcom list */ - if (!!platform->name == !!platform->of_node) { - dev_err(card->dev, - "ASoC: Neither/both platform name/of_node are set for %s\n", - link->name); - return -EINVAL; - } + if (snd_soc_dlc_component_is_invalid(dlc)) + goto component_invalid; + + if (snd_soc_dlc_component_is_empty(dlc)) + goto component_empty;
/* * Defer card registration if platform component is not added to * component list. */ - if (!soc_find_component(platform)) { - dev_dbg(card->dev, - "ASoC: platform component %s not found for link %s\n", - platform->name, link->name); - return -EPROBE_DEFER; - } + if (!soc_find_component(dlc)) + goto component_not_find; }
- for_each_link_cpus(link, i, cpu) { + /* CPU check */ + for_each_link_cpus(link, i, dlc) { /* * CPU device may be specified by either name or OF node, but * can be left unspecified, and will be matched based on DAI * name alone.. */ - if (cpu->name && cpu->of_node) { - dev_err(card->dev, - "ASoC: Neither/both cpu name/of_node are set for %s\n", - link->name); - return -EINVAL; - } + if (snd_soc_dlc_component_is_invalid(dlc)) + goto component_invalid;
- /* - * Defer card registration if cpu dai component is not added to - * component list. - */ - if ((cpu->of_node || cpu->name) && - !soc_find_component(cpu)) { - dev_dbg(card->dev, - "ASoC: cpu component %s not found for link %s\n", - cpu->name, link->name); - return -EPROBE_DEFER; - }
- /* - * At least one of CPU DAI name or CPU device name/node must be - * specified - */ - if (!cpu->dai_name && - !(cpu->name || cpu->of_node)) { - dev_err(card->dev, - "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", - link->name); - return -EINVAL; + if (snd_soc_dlc_component_is_empty(dlc)) { + /* + * At least one of CPU DAI name or CPU device name/node must be specified + */ + if (snd_soc_dlc_dai_is_empty(dlc)) + goto component_dai_empty; + } else { + /* + * Defer card registration if Component is not added + */ + if (!soc_find_component(dlc)) + goto component_not_find; } }
return 0; + +component_invalid: + dev_err(card->dev, "ASoC: Both Component name/of_node are set for %s\n", link->name); + return -EINVAL; + +component_empty: + dev_err(card->dev, "ASoC: Neither Component name/of_node are set for %s\n", link->name); + return -EINVAL; + +component_not_find: + dev_err(card->dev, "ASoC: Component %s not found for link %s\n", dlc->name, link->name); + return -EPROBE_DEFER; + +dai_empty: + dev_err(card->dev, "ASoC: DAI name is not set for %s\n", link->name); + return -EINVAL; + +component_dai_empty: + dev_err(card->dev, "ASoC: Neither DAI/Component name/of_node are set for %s\n", link->name); + return -EINVAL; }
/**
Current ASoC is specifying and checking DAI name. But where it came from and how to check was ambiguous. This patch adds snd_soc_dai_name_get() / snd_soc_dlc_dai_is_match() and makes it clear.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/soc-dai.h | 2 ++ sound/soc/soc-core.c | 62 +++++++++++++++++++++++++++++++---------- 2 files changed, 49 insertions(+), 15 deletions(-)
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index e3906ecda740..a4538040e88d 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -271,6 +271,8 @@ int snd_soc_dai_compr_get_metadata(struct snd_soc_dai *dai, struct snd_compr_stream *cstream, struct snd_compr_metadata *metadata);
+const char *snd_soc_dai_name_get(struct snd_soc_dai *dai); + struct snd_soc_dai_ops { /* * DAI clocking configuration, all optional. diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 94e856d67a46..ee309d3fe89c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -253,6 +253,47 @@ static inline int snd_soc_dlc_dai_is_empty(struct snd_soc_dai_link_component *dl return !dlc->dai_name; }
+static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc, + struct snd_soc_dai *dai) +{ + if (!dlc) + return 0; + + if (!dlc->dai_name) + return 1; + + /* see snd_soc_dai_name_get() */ + + if (strcmp(dlc->dai_name, dai->name) == 0) + return 1; + + if (dai->driver->name && + strcmp(dai->driver->name, dlc->dai_name) == 0) + return 1; + + if (dai->component->name && + strcmp(dlc->dai_name, dai->component->name) == 0) + return 1; + + return 0; +} + +const char *snd_soc_dai_name_get(struct snd_soc_dai *dai) +{ + /* see snd_soc_is_matching_dai() */ + if (dai->name) + return dai->name; + + if (dai->driver->name) + return dai->driver->name; + + if (dai->component->name) + return dai->component->name; + + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_name_get); + static int snd_soc_rtd_add_component(struct snd_soc_pcm_runtime *rtd, struct snd_soc_component *component) { @@ -810,18 +851,11 @@ struct snd_soc_dai *snd_soc_find_dai( lockdep_assert_held(&client_mutex);
/* Find CPU DAI from registered DAIs */ - for_each_component(component) { - if (!snd_soc_is_matching_component(dlc, component)) - continue; - for_each_component_dais(component, dai) { - if (dlc->dai_name && strcmp(dai->name, dlc->dai_name) - && (!dai->driver->name - || strcmp(dai->driver->name, dlc->dai_name))) - continue; - - return dai; - } - } + for_each_component(component) + if (snd_soc_is_matching_component(dlc, component)) + for_each_component_dais(component, dai) + if (snd_soc_is_matching_dai(dlc, dai)) + return dai;
return NULL; } @@ -3316,9 +3350,7 @@ int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_ id--; }
- dlc->dai_name = dai->driver->name; - if (!dlc->dai_name) - dlc->dai_name = pos->name; + dlc->dai_name = snd_soc_dai_name_get(dai); } else if (ret) { /* * if another error than ENOTSUPP is returned go on and
Current ASoC Card is using dlc (snd_soc_dai_link_component) to find target DAI / Component to be used. Current dlc has below 3 items to identify DAI / Component
(a) name for Component (b) of_node for Component (c) dai_name for DAI
(a) or (b) is used to identify target Component, and (c) is used to identify DAI.
One of the biggest issue on it today is dlc needs "name matching" for "dai_name" (c).
It was not a big deal when we were using platform_device, because we could specify nessesary "dai_name" via its platform_data.
But we need to find DAI name pointer from whole registered datas and/or each related driver somehow in case of DT, because we can't specify it. Therefore, Card driver parses DT and assumes the DAI, and find its name pointer. How to assume is based on each Component and/or Card.
Next biggest issue is Component node (a)/(b).
Basically, Component is registered when CPU/Codec driver was probed() (X). Here, 1 Component is possible to have some DAIs.
int xxx_probe(struct platform_device *pdev) { ... (X) ret = devm_snd_soc_register_component(pdev->dev, &component_driver, &dai_driver, dai_driver_num); ... }
The image of each data will be like below. One note here is "driver" is included for later explanation.
+-driver------+ |+-component-+| || dai0|| || dai1|| || ...|| |+-----------+| +-------------+
The point here is 1 driver has 1 Component, because basically driver calles snd_soc_register_component() (= X) once.
Here is the very basic CPU/Codec connection image.
HW image SW image +-- Board ------------+ +-card--------------------------+ |+-----+ +------+| |+-driver------+ +-driver------+| || CPU | <--> |CodecA|| ||+-component-+| |+-component-+|| |+-----+ +------+| ||| dai|<=>|dai ||| +---------------------+ ||+-----------+| |+-----------+|| |+-------------+ +-------------+| +-------------------------------+
It will be very complex if it has multi DAIs. Here is intuitive easy to understandable HW / SW example.
HW image SW image +-- Board ---------------+ +-card--------------------------+ |+--------+ +------+| |+-driver------+ +-driver------+| || CPU ch0| <--> |CodecA|| ||+-component-+| |+-component-+|| || | +------+| ||| ch0 dai|<=>|dai ||| || | +------+| ||| || |+-----------+|| || ch1| <--> |CodecB|| ||| || +-------------+| |+--------+ +------+| ||| || +-driver------+| +------------------------+ ||| || |+-component-+|| ||| ch1 dai|<=>|dai ||| ||+-----------+| |+-----------+|| |+-------------+ +-------------+| +-------------------------------+
It will be handled as multi interface as "one Card".
card0,0: CPU-ch0 - CodecA card0,1: CPU-ch1 - CodecB ^
But, here is the HW image example which will be more complex
+-- Basic Board ---------+ |+--------+ +------+| || CPU ch0| <--> |CodecA|| || ch1| <-+ +------+| |+--------+ | | +-------------|----------+ +-- expansion board -----+ | | +------+| | +->|CodecB|| | +------+| +------------------------+
We intuitively think we want to handle these as "2 Sound Cards".
card0,0: CPU-ch0 - CodecA card1,0: CPU-ch1 - CodecB ^
But below image which we can register today doesn't allow it, because the same Component will be connected to both Card0/1, but it will be rejected by (Z).
+-driver------+ |+-component-+| +-card0-------------------------+ ||| || +-driver------+| ||| || |+-component-+|| ||| ch0 dai|<=>|dai ||| ||| || |+-----------+|| ||| || +-------------+| +-------------------------------+ || || +-card1-------------------------+ ||| || +-driver------+| ||| || |+-component-+|| ||| ch1 dai|<=>|dai ||| ||| || |+-----------+|| ||| || +-------------+| +-------------------------------+ |+-----------+| +-------------+
static int soc_probe_component() { ... if (component->card) { (Z) if (component->card != card) { dev_err(component->dev, ...); return -ENODEV; } return 0; } ... }
So, how about to call snd_soc_register_component() (= X) multiple times on probe() to avoid buplicated component->card limitation, to be like below ?
+-driver------+ +-card0-------------------------+ || | +-driver------+| ||+-component-+| |+-component-+|| ||| ch0 dai|<=>|dai ||| ||+-----------+| |+-----------+|| || | +-------------+| +-------------------------------+ | | +-card1-------------------------+ || | +-driver------+| ||+-component-+| |+-component-+|| ||| ch1 dai|<=>|dai ||| ||+-----------+| |+-----------+|| || | +-------------+| +-------------------------------+ +-------------+
Yes, looks good. But unfortunately it doesn't help us for now. Let's see soc_component_to_node() and snd_soc_is_matching_component()
static struct device_node *soc_component_to_node(struct snd_soc_component *component) { ... (A) of_node = component->dev->of_node; ... }
static int snd_soc_is_matching_component(...) { ... (B) if (dlc->of_node && component_of_node != dlc->of_node) ... }
dlc checkes "of_node" to identify target component (B), but this "of_node" came from component->dev (A) which is added by snd_soc_register_component() (X) on probe().
This means we can have different "component->card", but have same "component->dev" in this case.
Even though we calls snd_soc_register_component() (= X) multiple times, all Components have same driver's dev, thus it is impossible to identified the Component. And if it was impossible to identify Component, it is impossible to identify DAI on current implementation.
So, how to handle above complex HW image today is 2 patterns. One is handles it as "1 big sound card". The SW image is like below.
SW image +-card--------------------------+ |+-driver------+ +-driver------+| ||+-component-+| |+-component-+|| ||| ch0 dai|<=>|dai ||| ||| || |+-----------+|| ||| || +-------------+| ||| || +-driver------+| ||| || |+-component-+|| ||| ch1 dai|<->|dai ||| ||+-----------+| |+-----------+|| |+-------------+ +-------------+| +-------------------------------+
But the problem is not intuitive. We want to handle it as "2 Cards".
2nd pattern is like below.
SW image +-card0-------------------------+ |+-driver------+ +-driver------+| ||+-component-+| |+-component-+|| ||| ch0 dai|<=>|dai ||| ||+-----------+| |+-----------+|| |+-------------+ +-------------+| +-------------------------------+
+-card1-------------------------+ |+-driver------+ +-driver------+| ||+-component-+| |+-component-+|| ||| ch1 dai|<=>|dai ||| ||+-----------+| |+-----------+|| |+-------------+ +-------------+| +-------------------------------+
It handles as "2 Cards", but CPU part needs to be probed as 2 drivers. It is also not intuitive.
To solve this issue, we need to have multi Component support.
In current implementation, we need to identify Component first to identify DAI, and it is using name matching to identify DAI.
But how about to be enable to directly identify DAI by unique way instead of name matching ? In such case, we can directly identify DAI, then it can identify Component from DAI.
For example Simple-Card / Audio-Graph-Card case, it is specifying DAI via its node.
Simple-Card
sound-dai = <&cpu-sound>;
Audio-Graph-Card
dais = <&cpu-sound>;
If each CPU/Codec driver keeps this property when probing, we can identify DAI directly from Card. Being able to identify DAI directly means being able to identify its Component as well even though Component has same dev (= B).
This patch adds new "dai_node" for it.
To keeping compatibility, it checks "dai_node" first if it has, otherwise, use existing method (name matching).
Link: https://lore.kernel.org/r/87fskz5yrr.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/soc-dai.h | 1 + include/sound/soc.h | 1 + sound/soc/soc-core.c | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index a4538040e88d..a33d803fe548 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -399,6 +399,7 @@ struct snd_soc_dai_driver { unsigned int id; unsigned int base; struct snd_soc_dobj dobj; + struct of_phandle_args *dai_args;
/* DAI driver callbacks */ int (*probe)(struct snd_soc_dai *dai); diff --git a/include/sound/soc.h b/include/sound/soc.h index b27f84580c5b..dda731795bd4 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -651,6 +651,7 @@ struct snd_soc_dai_link_component { const char *name; struct device_node *of_node; const char *dai_name; + struct of_phandle_args *dai_args; };
struct snd_soc_dai_link_codec_ch_map { diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ee309d3fe89c..8487a4c12753 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -238,9 +238,25 @@ static inline void snd_soc_debugfs_exit(void) { }
#endif
+static int snd_soc_is_match_dai_args(struct of_phandle_args *args1, + struct of_phandle_args *args2) +{ + if (!args1 || !args2) + return 0; + + if (args1->np != args2->np) + return 0; + + for (int i = 0; i < args1->args_count; i++) + if (args1->args[i] != args2->args[i]) + return 0; + + return 1; +} + static inline int snd_soc_dlc_component_is_empty(struct snd_soc_dai_link_component *dlc) { - return !(dlc->name || dlc->of_node); + return !(dlc->dai_args || dlc->name || dlc->of_node); }
static inline int snd_soc_dlc_component_is_invalid(struct snd_soc_dai_link_component *dlc) @@ -250,7 +266,7 @@ static inline int snd_soc_dlc_component_is_invalid(struct snd_soc_dai_link_compo
static inline int snd_soc_dlc_dai_is_empty(struct snd_soc_dai_link_component *dlc) { - return !dlc->dai_name; + return !(dlc->dai_args || dlc->dai_name); }
static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc, @@ -259,6 +275,9 @@ static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc, if (!dlc) return 0;
+ if (dlc->dai_args) + return snd_soc_is_match_dai_args(dai->driver->dai_args, dlc->dai_args); + if (!dlc->dai_name) return 1;
@@ -799,6 +818,15 @@ static int snd_soc_is_matching_component( if (!dlc) return 0;
+ if (dlc->dai_args) { + struct snd_soc_dai *dai; + + for_each_component_dais(component, dai) + if (snd_soc_is_matching_dai(dlc, dai)) + return 1; + return 0; + } + component_of_node = soc_component_to_node(component);
if (dlc->of_node && component_of_node != dlc->of_node)
To enable multi Component, Card driver need to get DAI via dai_args to identify it. This patch adds snd_soc_get_dai_via_args() for it.
This is helper function for multi Component support.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/soc.h | 1 + sound/soc/soc-core.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+)
diff --git a/include/sound/soc.h b/include/sound/soc.h index dda731795bd4..1b3c58fe14c4 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1336,6 +1336,7 @@ int snd_soc_add_pcm_runtimes(struct snd_soc_card *card, void snd_soc_remove_pcm_runtime(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd);
+struct snd_soc_dai *snd_soc_get_dai_via_args(struct of_phandle_args *dai_args); struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv, bool legacy_dai_naming); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 8487a4c12753..22a065f4c908 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3446,6 +3446,24 @@ int snd_soc_of_get_dai_name(struct device_node *of_node, } EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name);
+struct snd_soc_dai *snd_soc_get_dai_via_args(struct of_phandle_args *dai_args) +{ + struct snd_soc_dai *dai; + struct snd_soc_component *component; + + mutex_lock(&client_mutex); + for_each_component(component) { + for_each_component_dais(component, dai) + if (snd_soc_is_match_dai_args(dai->driver->dai_args, dai_args)) + goto found; + } + dai = NULL; +found: + mutex_unlock(&client_mutex); + return dai; +} +EXPORT_SYMBOL_GPL(snd_soc_get_dai_via_args); + static void __snd_soc_of_put_component(struct snd_soc_dai_link_component *component) { if (component->of_node) {
Current snd_soc_is_matching_component() checks "of_node" or "dai_args". Thus coping "of_node" only is not enough to use CPU as Platform. This patch adds snd_soc_dlc_use_cpu_as_platform() and help it.
This is helper function for multi Component support.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/soc.h | 2 ++ sound/soc/generic/simple-card-utils.c | 2 +- sound/soc/soc-core.c | 8 ++++++++ 3 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/include/sound/soc.h b/include/sound/soc.h index 1b3c58fe14c4..94fca10f01ad 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1336,6 +1336,8 @@ int snd_soc_add_pcm_runtimes(struct snd_soc_card *card, void snd_soc_remove_pcm_runtime(struct snd_soc_card *card, struct snd_soc_pcm_runtime *rtd);
+void snd_soc_dlc_use_cpu_as_platform(struct snd_soc_dai_link_component *platforms, + struct snd_soc_dai_link_component *cpus); struct snd_soc_dai *snd_soc_get_dai_via_args(struct of_phandle_args *dai_args); struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv, diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 3019626b0592..c142571992a1 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -649,7 +649,7 @@ void asoc_simple_canonicalize_platform(struct snd_soc_dai_link_component *platfo * simple-card.c :: simple_count_noml() */ if (!platforms->of_node) - platforms->of_node = cpus->of_node; + snd_soc_dlc_use_cpu_as_platform(platforms, cpus); } EXPORT_SYMBOL_GPL(asoc_simple_canonicalize_platform);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 22a065f4c908..9fd7e633d374 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3005,6 +3005,14 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot);
+void snd_soc_dlc_use_cpu_as_platform(struct snd_soc_dai_link_component *platforms, + struct snd_soc_dai_link_component *cpus) +{ + platforms->of_node = cpus->of_node; + platforms->dai_args = cpus->dai_args; +} +EXPORT_SYMBOL_GPL(snd_soc_dlc_use_cpu_as_platform); + void snd_soc_of_parse_node_prefix(struct device_node *np, struct snd_soc_codec_conf *codec_conf, struct device_node *of_node,
To use multi Component support, we need to check dai_args whether Card could get DAI from args (CPU/Codec needs set dai_args on DAI driver). If it could, we need to allocate dai_args for dlc. This patch adds snd_soc_copy_dai_args() for it.
This is helper function for multi Component support.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/soc.h | 2 ++ sound/soc/soc-core.c | 13 +++++++++++++ 2 files changed, 15 insertions(+)
diff --git a/include/sound/soc.h b/include/sound/soc.h index 94fca10f01ad..fa2337a3cf4c 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1338,6 +1338,8 @@ void snd_soc_remove_pcm_runtime(struct snd_soc_card *card,
void snd_soc_dlc_use_cpu_as_platform(struct snd_soc_dai_link_component *platforms, struct snd_soc_dai_link_component *cpus); +struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev, + struct of_phandle_args *args); struct snd_soc_dai *snd_soc_get_dai_via_args(struct of_phandle_args *dai_args); struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component, struct snd_soc_dai_driver *dai_drv, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 9fd7e633d374..a5b96c17633a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -809,6 +809,19 @@ static struct device_node return of_node; }
+struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev, struct of_phandle_args *args) +{ + struct of_phandle_args *ret = devm_kzalloc(dev, sizeof(*ret), GFP_KERNEL); + + if (!ret) + return NULL; + + *ret = *args; + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_copy_dai_args); + static int snd_soc_is_matching_component( const struct snd_soc_dai_link_component *dlc, struct snd_soc_component *component)
If CPU/Codec driver keeps its DAI node, we can directly identify actual DAI by using snd_soc_get_dai_via_args(). This means we can use multi Component.
This patch enables multi Component support on Audio Graph Card/Card2.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/simple_card_utils.h | 5 ++--- sound/soc/generic/audio-graph-card.c | 2 +- sound/soc/generic/audio-graph-card2.c | 2 +- sound/soc/generic/simple-card-utils.c | 21 ++++++++++++++++++--- 4 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index b450d5873227..d1a95bc33c56 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -192,9 +192,8 @@ int asoc_simple_remove(struct platform_device *pdev);
int asoc_graph_card_probe(struct snd_soc_card *card); int asoc_graph_is_ports0(struct device_node *port); -int asoc_graph_parse_dai(struct device_node *ep, - struct snd_soc_dai_link_component *dlc, - int *is_single_link); +int asoc_graph_parse_dai(struct device *dev, struct device_node *ep, + struct snd_soc_dai_link_component *dlc, int *is_single_link);
#ifdef DEBUG static inline void asoc_simple_debug_dai(struct asoc_simple_priv *priv, diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index c6e0f9132193..0b8258b6bd8e 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -126,7 +126,7 @@ static int graph_parse_node(struct asoc_simple_priv *priv,
graph_parse_mclk_fs(top, ep, dai_props);
- ret = asoc_graph_parse_dai(ep, dlc, cpu); + ret = asoc_graph_parse_dai(dev, ep, dlc, cpu); if (ret < 0) return ret;
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 542c4a114940..98732468a992 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -407,7 +407,7 @@ static int __graph_parse_node(struct asoc_simple_priv *priv,
graph_parse_mclk_fs(ep, dai_props);
- ret = asoc_graph_parse_dai(ep, dlc, &is_single_links); + ret = asoc_graph_parse_dai(dev, ep, dlc, &is_single_links); if (ret < 0) return ret;
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index c142571992a1..5b18a4af022f 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -1066,12 +1066,12 @@ static int graph_get_dai_id(struct device_node *ep) return id; }
-int asoc_graph_parse_dai(struct device_node *ep, - struct snd_soc_dai_link_component *dlc, - int *is_single_link) +int asoc_graph_parse_dai(struct device *dev, struct device_node *ep, + struct snd_soc_dai_link_component *dlc, int *is_single_link) { struct device_node *node; struct of_phandle_args args = {}; + struct snd_soc_dai *dai; int ret;
if (!ep) @@ -1079,6 +1079,20 @@ int asoc_graph_parse_dai(struct device_node *ep,
node = of_graph_get_port_parent(ep);
+ /* + * Try to find from DAI node + */ + args.np = ep; + dai = snd_soc_get_dai_via_args(&args); + if (dai) { + dlc->dai_name = snd_soc_dai_name_get(dai); + dlc->dai_args = snd_soc_copy_dai_args(dev, &args); + if (!dlc->dai_args) + return -ENOMEM; + + goto parse_dai_end; + } + /* Get dai->name */ args.np = node; args.args[0] = graph_get_dai_id(ep); @@ -1109,6 +1123,7 @@ int asoc_graph_parse_dai(struct device_node *ep, return ret; }
+parse_dai_end: if (is_single_link) *is_single_link = of_graph_get_endpoint_count(node) == 1;
If CPU/Codec driver keeps its DAI node, we can directly identify actual DAI by using snd_soc_get_dai_via_args(). This means we can use multi Component.
This patch enables multi Component support for Simple Card
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/generic/simple-card.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 0745bf6a09aa..9470b1e67f9e 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -52,11 +52,13 @@ static int asoc_simple_parse_platform(struct device_node *node, return 0; }
-static int asoc_simple_parse_dai(struct device_node *node, +static int asoc_simple_parse_dai(struct device *dev, + struct device_node *node, struct snd_soc_dai_link_component *dlc, int *is_single_link) { struct of_phandle_args args; + struct snd_soc_dai *dai; int ret;
if (!node) @@ -70,6 +72,19 @@ static int asoc_simple_parse_dai(struct device_node *node, if (ret) return ret;
+ /* + * Try to find from DAI args + */ + dai = snd_soc_get_dai_via_args(&args); + if (dai) { + dlc->dai_name = snd_soc_dai_name_get(dai); + dlc->dai_args = snd_soc_copy_dai_args(dev, &args); + if (!dlc->dai_args) + return -ENOMEM; + + goto parse_dai_end; + } + /* * FIXME * @@ -93,6 +108,7 @@ static int asoc_simple_parse_dai(struct device_node *node, if (ret < 0) return ret;
+parse_dai_end: if (is_single_link) *is_single_link = !args.args_count;
@@ -156,7 +172,7 @@ static int simple_parse_node(struct asoc_simple_priv *priv,
simple_parse_mclk_fs(top, np, dai_props, prefix);
- ret = asoc_simple_parse_dai(np, dlc, cpu); + ret = asoc_simple_parse_dai(dev, np, dlc, cpu); if (ret) return ret;
Current rsnd is using DAI ID to get own priv data without setting driver->id. It was no problem for Single Component, but will be problem in case of Multi Component, because it is not a DAI serial number.
struct snd_soc_dai *snd_soc_register_dai(...) { ... if (dai_drv->id) dai->id = dai_drv->id; else dai->id = component->num_dai; ... }
This patch sets driver->id, and get serial number.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/core.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 6a522e6dd85a..f3f17b784025 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1378,6 +1378,7 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, drv->name = rdai->name; drv->ops = &rsnd_soc_dai_ops; drv->pcm_new = rsnd_pcm_new; + drv->id = dai_i;
io_playback->rdai = rdai; io_capture->rdai = rdai;
It calls rsnd_dai_of_node() to know it was called from Audio Graph Card/Card2, or from Simple Audio Card. And after that, it gets number of related DAIs.
To be more simple code, this patch merges these. This is prepare for multi Component support.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/core.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index f3f17b784025..a87d7fa55f7b 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1261,7 +1261,7 @@ int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name }
static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv, - int *is_graph) + int *nr, int *is_graph) { struct device *dev = rsnd_priv_to_dev(priv); struct device_node *np = dev->of_node; @@ -1274,22 +1274,30 @@ static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv, * parse both previous dai (= rcar_sound,dai), and * graph dai (= ports/port) */ + + /* + * Simple-Card + */ dai_node = of_get_child_by_name(np, RSND_NODE_DAI); if (dai_node) { + *nr = of_get_child_count(dai_node); ret = dai_node; goto of_node_compatible; }
- ret = np; - + /* + * Audio-Graph-Card + */ dai_node = of_graph_get_next_endpoint(np, NULL); - if (dai_node) - goto of_node_graph; + if (dai_node) { + *nr = of_graph_get_endpoint_count(np); + *is_graph = 1; + ret = np; + goto of_node_compatible; + }
return NULL;
-of_node_graph: - *is_graph = 1; of_node_compatible: of_node_put(dai_node);
@@ -1447,16 +1455,11 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) struct snd_soc_dai_driver *rdrv; struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_dai *rdai; - int nr; + int nr = 0; int is_graph; int dai_i;
- dai_node = rsnd_dai_of_node(priv, &is_graph); - if (is_graph) - nr = of_graph_get_endpoint_count(dai_node); - else - nr = of_get_child_count(dai_node); - + dai_node = rsnd_dai_of_node(priv, &nr, &is_graph); if (!nr) return -EINVAL;
+-- Basic Board ---------+ |+--------+ +------+| || CPU ch0| <--> |CodecA|| || ch1| <-+ +------+| |+--------+ | | +-------------|----------+ +-- expansion board -----+ | | +------+| | +->|CodecB|| | +------+| +------------------------+
In above HW connection case, we intuitively think we want to handle these as "2 Sound Cards".
card0,0: CPU-ch0 - CodecA card1,0: CPU-ch1 - CodecB
But, we needed to handle it as "1 big Sound Card", because of Component vs Card limitation.
card0,0: CPU-ch0 - CodecA card0,1: CPU-ch1 - CodecB
This patch enables multi Component to handle multi Cards. To support it, it needs
- Fill dai_args for each DAI on snd_soc_dai_driver - Parse DT for each Component (Simple Card/Audio Graph Card)
Ex) Simple Card
rcar_sound { ...
/* Component0 */ rcar_sound,dai@0 { ... };
/* Component1 */ rcar_sound,dai@1 { ... }; };
Ex) Audio Graph Card/Card2
rcar_sound { /* Component0 */ ports@0 { ... };
/* Component1 */ ports@1 { ... }; };
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/sh/rcar/core.c | 134 ++++++++++++++++++++++++++------------- sound/soc/sh/rcar/rsnd.h | 4 ++ 2 files changed, 95 insertions(+), 43 deletions(-)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index a87d7fa55f7b..9f3d97bc177a 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1260,13 +1260,13 @@ int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name return i; }
-static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv, - int *nr, int *is_graph) +static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph) { struct device *dev = rsnd_priv_to_dev(priv); struct device_node *np = dev->of_node; - struct device_node *dai_node; - struct device_node *ret; + struct device_node *ports, *node; + int nr = 0; + int i = 0;
*is_graph = 0;
@@ -1278,30 +1278,47 @@ static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv, /* * Simple-Card */ - dai_node = of_get_child_by_name(np, RSND_NODE_DAI); - if (dai_node) { - *nr = of_get_child_count(dai_node); - ret = dai_node; - goto of_node_compatible; + node = of_get_child_by_name(np, RSND_NODE_DAI); + if (!node) + goto audio_graph; + + of_node_put(node); + + for_each_child_of_node(np, node) { + if (!of_node_name_eq(node, RSND_NODE_DAI)) + continue; + + priv->component_dais[i] = of_get_child_count(node); + nr += priv->component_dais[i]; + i++; + if (i >= RSND_MAX_COMPONENT) { + dev_info(dev, "reach to max component\n"); + break; + } }
+ return nr; + +audio_graph: /* * Audio-Graph-Card */ - dai_node = of_graph_get_next_endpoint(np, NULL); - if (dai_node) { - *nr = of_graph_get_endpoint_count(np); - *is_graph = 1; - ret = np; - goto of_node_compatible; + for_each_child_of_node(np, ports) { + if (!of_node_name_eq(ports, "ports") && + !of_node_name_eq(ports, "port")) + continue; + priv->component_dais[i] = of_graph_get_endpoint_count(ports); + nr += priv->component_dais[i]; + i++; + if (i >= RSND_MAX_COMPONENT) { + dev_info(dev, "reach to max component\n"); + break; + } }
- return NULL; - -of_node_compatible: - of_node_put(dai_node); + *is_graph = 1;
- return ret; + return nr; }
@@ -1365,6 +1382,8 @@ static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd,
static void __rsnd_dai_probe(struct rsnd_priv *priv, struct device_node *dai_np, + struct device_node *node_np, + uint32_t node_arg, int dai_i) { struct rsnd_dai_stream *io_playback; @@ -1382,11 +1401,17 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv,
snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i);
+ /* for multi Component */ + rdai->dai_args.np = node_np; + rdai->dai_args.args_count = 1; + rdai->dai_args.args[0] = node_arg; + rdai->priv = priv; drv->name = rdai->name; drv->ops = &rsnd_soc_dai_ops; drv->pcm_new = rsnd_pcm_new; drv->id = dai_i; + drv->dai_args = &rdai->dai_args;
io_playback->rdai = rdai; io_capture->rdai = rdai; @@ -1450,16 +1475,15 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv,
static int rsnd_dai_probe(struct rsnd_priv *priv) { - struct device_node *dai_node; - struct device_node *dai_np; struct snd_soc_dai_driver *rdrv; struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *np = dev->of_node; struct rsnd_dai *rdai; int nr = 0; int is_graph; int dai_i;
- dai_node = rsnd_dai_of_node(priv, &nr, &is_graph); + nr = rsnd_dai_of_node(priv, &is_graph); if (!nr) return -EINVAL;
@@ -1477,26 +1501,42 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) */ dai_i = 0; if (is_graph) { - for_each_endpoint_of_node(dai_node, dai_np) { - __rsnd_dai_probe(priv, dai_np, dai_i); - if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) { - rdai = rsnd_rdai_get(priv, dai_i); - - rsnd_parse_connect_graph(priv, &rdai->playback, dai_np); - rsnd_parse_connect_graph(priv, &rdai->capture, dai_np); + struct device_node *ports; + struct device_node *dai_np; + + for_each_child_of_node(np, ports) { + if (!of_node_name_eq(ports, "ports") && + !of_node_name_eq(ports, "port")) + continue; + for_each_endpoint_of_node(ports, dai_np) { + __rsnd_dai_probe(priv, dai_np, dai_np, 0, dai_i); + if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) { + rdai = rsnd_rdai_get(priv, dai_i); + + rsnd_parse_connect_graph(priv, &rdai->playback, dai_np); + rsnd_parse_connect_graph(priv, &rdai->capture, dai_np); + } + dai_i++; } - dai_i++; } } else { - for_each_child_of_node(dai_node, dai_np) { - __rsnd_dai_probe(priv, dai_np, dai_i); - if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) { - rdai = rsnd_rdai_get(priv, dai_i); + struct device_node *node; + struct device_node *dai_np; + + for_each_child_of_node(np, node) { + if (!of_node_name_eq(node, RSND_NODE_DAI)) + continue;
- rsnd_parse_connect_simple(priv, &rdai->playback, dai_np); - rsnd_parse_connect_simple(priv, &rdai->capture, dai_np); + for_each_child_of_node(node, dai_np) { + __rsnd_dai_probe(priv, dai_np, np, dai_i, dai_i); + if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) { + rdai = rsnd_rdai_get(priv, dai_i); + + rsnd_parse_connect_simple(priv, &rdai->playback, dai_np); + rsnd_parse_connect_simple(priv, &rdai->capture, dai_np); + } + dai_i++; } - dai_i++; } }
@@ -1926,6 +1966,7 @@ static int rsnd_probe(struct platform_device *pdev) rsnd_dai_probe, }; int ret, i; + int ci;
/* * init priv data @@ -1962,11 +2003,18 @@ static int rsnd_probe(struct platform_device *pdev) /* * asoc register */ - ret = devm_snd_soc_register_component(dev, &rsnd_soc_component, - priv->daidrv, rsnd_rdai_nr(priv)); - if (ret < 0) { - dev_err(dev, "cannot snd dai register\n"); - goto exit_snd_probe; + ci = 0; + for (i = 0; priv->component_dais[i] > 0; i++) { + int nr = priv->component_dais[i]; + + ret = devm_snd_soc_register_component(dev, &rsnd_soc_component, + priv->daidrv + ci, nr); + if (ret < 0) { + dev_err(dev, "cannot snd component register\n"); + goto exit_snd_probe; + } + + ci += nr; }
pm_runtime_enable(dev); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 239705d52517..43c0d675cc34 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -545,6 +545,7 @@ struct rsnd_dai { struct rsnd_dai_stream capture; struct rsnd_priv *priv; struct snd_pcm_hw_constraint_list constraint; + struct of_phandle_args dai_args;
int max_channels; /* 2ch - 16ch */ int ssi_lane; /* 1lane - 4lane */ @@ -702,6 +703,9 @@ struct rsnd_priv { struct snd_soc_dai_driver *daidrv; struct rsnd_dai *rdai; int rdai_nr; + +#define RSND_MAX_COMPONENT 3 + int component_dais[RSND_MAX_COMPONENT]; };
#define rsnd_priv_to_pdev(priv) ((priv)->pdev)
renesas,rsnd uses both "ports" has "port", and these are very similar. To avoid duplicated definitions, this patch adds common port-def.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- .../bindings/sound/renesas,rsnd.yaml | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml index 8a821dec9526..d9808b130e8d 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml @@ -9,6 +9,20 @@ title: Renesas R-Car Sound Driver maintainers: - Kuninori Morimoto kuninori.morimoto.gx@renesas.com
+definitions: + port-def: + $ref: audio-graph-port.yaml#/definitions/port-base + unevaluatedProperties: false + patternProperties: + "^endpoint(@[0-9a-f]+)?": + $ref: audio-graph-port.yaml#/definitions/endpoint-base + properties: + playback: + $ref: /schemas/types.yaml#/definitions/phandle-array + capture: + $ref: /schemas/types.yaml#/definitions/phandle-array + unevaluatedProperties: false + properties:
compatible: @@ -108,30 +122,10 @@ properties: unevaluatedProperties: false patternProperties: '^port(@[0-9a-f]+)?$': - $ref: audio-graph-port.yaml#/definitions/port-base - unevaluatedProperties: false - patternProperties: - "^endpoint(@[0-9a-f]+)?": - $ref: audio-graph-port.yaml#/definitions/endpoint-base - properties: - playback: - $ref: /schemas/types.yaml#/definitions/phandle-array - capture: - $ref: /schemas/types.yaml#/definitions/phandle-array - unevaluatedProperties: false + $ref: "#/definitions/port-def"
port: - $ref: audio-graph-port.yaml#/definitions/port-base - unevaluatedProperties: false - patternProperties: - "^endpoint(@[0-9a-f]+)?": - $ref: audio-graph-port.yaml#/definitions/endpoint-base - properties: - playback: - $ref: /schemas/types.yaml#/definitions/phandle-array - capture: - $ref: /schemas/types.yaml#/definitions/phandle-array - unevaluatedProperties: false + $ref: "#/definitions/port-def"
rcar_sound,dvc: description: DVC subnode.
On Mon, Jul 10, 2023 at 10:21:42AM +0900, Kuninori Morimoto wrote:
renesas,rsnd uses both "ports" has "port", and these are very similar. To avoid duplicated definitions, this patch adds common port-def.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
.../bindings/sound/renesas,rsnd.yaml | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml index 8a821dec9526..d9808b130e8d 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml @@ -9,6 +9,20 @@ title: Renesas R-Car Sound Driver maintainers:
- Kuninori Morimoto kuninori.morimoto.gx@renesas.com
+definitions:
$defs
- port-def:
- $ref: audio-graph-port.yaml#/definitions/port-base
- unevaluatedProperties: false
- patternProperties:
"^endpoint(@[0-9a-f]+)?":
$ref: audio-graph-port.yaml#/definitions/endpoint-base
properties:
playback:
$ref: /schemas/types.yaml#/definitions/phandle-array
capture:
$ref: /schemas/types.yaml#/definitions/phandle-array
unevaluatedProperties: false
properties:
compatible: @@ -108,30 +122,10 @@ properties: unevaluatedProperties: false patternProperties: '^port(@[0-9a-f]+)?$':
$ref: audio-graph-port.yaml#/definitions/port-base
unevaluatedProperties: false
patternProperties:
"^endpoint(@[0-9a-f]+)?":
$ref: audio-graph-port.yaml#/definitions/endpoint-base
properties:
playback:
$ref: /schemas/types.yaml#/definitions/phandle-array
capture:
$ref: /schemas/types.yaml#/definitions/phandle-array
unevaluatedProperties: false
$ref: "#/definitions/port-def"
port:
- $ref: audio-graph-port.yaml#/definitions/port-base
- unevaluatedProperties: false
- patternProperties:
"^endpoint(@[0-9a-f]+)?":
$ref: audio-graph-port.yaml#/definitions/endpoint-base
properties:
playback:
$ref: /schemas/types.yaml#/definitions/phandle-array
capture:
$ref: /schemas/types.yaml#/definitions/phandle-array
unevaluatedProperties: false
- $ref: "#/definitions/port-def"
It would be better if users just always used "ports { port {}; };" even in the single port case.
rcar_sound,dvc: description: DVC subnode. -- 2.25.1
Hi Rob
Thank you for reviewing
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml index 8a821dec9526..d9808b130e8d 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml @@ -9,6 +9,20 @@ title: Renesas R-Car Sound Driver maintainers:
- Kuninori Morimoto kuninori.morimoto.gx@renesas.com
+definitions:
$defs
Hmm... many drivers are using "definitions" ? This kind of indication is very confusable...
port:
- $ref: audio-graph-port.yaml#/definitions/port-base
- unevaluatedProperties: false
- patternProperties:
"^endpoint(@[0-9a-f]+)?":
$ref: audio-graph-port.yaml#/definitions/endpoint-base
properties:
playback:
$ref: /schemas/types.yaml#/definitions/phandle-array
capture:
$ref: /schemas/types.yaml#/definitions/phandle-array
unevaluatedProperties: false
- $ref: "#/definitions/port-def"
It would be better if users just always used "ports { port {}; };" even in the single port case.
"ports" support is added, we can't modify past.
Thank you for your help !!
Best regards --- Kuninori Morimoto
On Tue, Jul 11, 2023 at 09:00:18AM +0900, Kuninori Morimoto wrote:
Hi Rob
Thank you for reviewing
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml index 8a821dec9526..d9808b130e8d 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml @@ -9,6 +9,20 @@ title: Renesas R-Car Sound Driver maintainers:
- Kuninori Morimoto kuninori.morimoto.gx@renesas.com
+definitions:
$defs
Hmm... many drivers are using "definitions" ?
Patches welcome to change them. I don't think it's many (not counting types.yaml). Use of "definitions" or "$defs" is an exception.
This kind of indication is very confusable...
"definitions" was common convention in early json-schema. "$defs" was added to the specification to standardize it.
port:
- $ref: audio-graph-port.yaml#/definitions/port-base
- unevaluatedProperties: false
- patternProperties:
"^endpoint(@[0-9a-f]+)?":
$ref: audio-graph-port.yaml#/definitions/endpoint-base
properties:
playback:
$ref: /schemas/types.yaml#/definitions/phandle-array
capture:
$ref: /schemas/types.yaml#/definitions/phandle-array
unevaluatedProperties: false
- $ref: "#/definitions/port-def"
It would be better if users just always used "ports { port {}; };" even in the single port case.
"ports" support is added, we can't modify past.
I don't understand. The graph APIs all work the same with or without 'ports'. Changing the DT shouldn't affect the ABI.
Rob
Hi Rob
$defs
Hmm... many drivers are using "definitions" ?
Patches welcome to change them. I don't think it's many (not counting types.yaml). Use of "definitions" or "$defs" is an exception.
OK, let's use "$defs" in v3
It would be better if users just always used "ports { port {}; };" even in the single port case.
"ports" support is added, we can't modify past.
I don't understand. The graph APIs all work the same with or without 'ports'. Changing the DT shouldn't affect the ABI.
But, the driver didn't have "ports" handling code, "port" only. From "DT point of view" changing port -> ports { port {}; }; is no problem, but "driver code point of view", it is big problem.
Thank you for your help !!
Best regards --- Kuninori Morimoto
Hi Rob, Mark
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml index 8a821dec9526..d9808b130e8d 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml @@ -9,6 +9,20 @@ title: Renesas R-Car Sound Driver maintainers:
- Kuninori Morimoto kuninori.morimoto.gx@renesas.com
+definitions:
$defs
Hmm... many drivers are using "definitions" ?
Patches welcome to change them. I don't think it's many (not counting types.yaml). Use of "definitions" or "$defs" is an exception.
I will get error if I used "$defs" instead of "definitions", like below. There is no error in "definitions" case.
Mark Thus, I have no update for remainings (= [11/15] - [15/15]).
> pip3 list | grep jsonschema jsonschema 4.17.0 > pip3 list | grep dtschema dtschema 2023.6.1
> make dtbs_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/sound/renesas,rsnd.yaml LINT Documentation/devicetree/bindings CHKDT Documentation/devicetree/bindings/processed-schema.json /opt/disk_cd/morimoto/linux/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml: Unresolvable JSON pointer: 'definitions/port-def' SCHEMA Documentation/devicetree/bindings/processed-schema.json Traceback (most recent call last): File "/usr/local/bin/dt-mk-schema", line 38, in <module> schemas = dtschema.DTValidator(args.schemas).schemas File "/usr/local/lib/python3.8/dist-packages/dtschema/validator.py", line 355, in __init__ self.make_property_type_cache() File "/usr/local/lib/python3.8/dist-packages/dtschema/validator.py", line 406, in make_property_type_cache self.props, self.pat_props = get_prop_types(self.schemas) File "/usr/local/lib/python3.8/dist-packages/dtschema/validator.py", line 173, in get_prop_types props = extract_types(schemas) File "/usr/local/lib/python3.8/dist-packages/dtschema/validator.py", line 165, in extract_types _extract_subschema_types(props, sch, sch) File "/usr/local/lib/python3.8/dist-packages/dtschema/validator.py", line 159, in _extract_subschema_types _extract_prop_type(props, schema, p, v, k == 'patternProperties') File "/usr/local/lib/python3.8/dist-packages/dtschema/validator.py", line 52, in _extract_prop_type tmp_subschema = tmp_subschema[p] KeyError: 'definitions' make[2]: *** [/opt/disk_cd/morimoto/linux/Documentation/devicetree/bindings/Makefile:68: Documentation/devicetree/bindings/processed-schema.json] エ ラー 1 make[2]: *** ファイル 'Documentation/devicetree/bindings/processed-schema.json' を削除します make[1]: *** [/opt/disk_cd/morimoto/linux/Makefile:1500: dt_binding_check] エラー 2 make: *** [/opt/disk_cd/morimoto/linux/Makefile:234: __sub-make] エラー 2
Thank you for your help !!
Best regards --- Kuninori Morimoto
On Wed, Jul 19, 2023 at 5:36 PM Kuninori Morimoto kuninori.morimoto.gx@renesas.com wrote:
Hi Rob, Mark
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml index 8a821dec9526..d9808b130e8d 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml @@ -9,6 +9,20 @@ title: Renesas R-Car Sound Driver maintainers:
- Kuninori Morimoto kuninori.morimoto.gx@renesas.com
+definitions:
$defs
Hmm... many drivers are using "definitions" ?
Patches welcome to change them. I don't think it's many (not counting types.yaml). Use of "definitions" or "$defs" is an exception.
I will get error if I used "$defs" instead of "definitions", like below.
Are you sure you used $defs?
There is no error in "definitions" case.
Mark Thus, I have no update for remainings (= [11/15] - [15/15]).
> pip3 list | grep jsonschema jsonschema 4.17.0 > pip3 list | grep dtschema dtschema 2023.6.1 > make dtbs_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/sound/renesas,rsnd.yaml LINT Documentation/devicetree/bindings CHKDT Documentation/devicetree/bindings/processed-schema.json /opt/disk_cd/morimoto/linux/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml: Unresolvable JSON pointer: 'definitions/port-def'
Doesn't look like it. You have a ref to 'definitions/port-def'.
Rob
To enable multi Component support, "multi ports" is needed for Audio Graph Card/Card2, and "multi rcar_sound,dai" is needed for Simple Audio Card. This patch enable these.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- .../bindings/sound/renesas,rsnd.yaml | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml index d9808b130e8d..13a5a0a10fe6 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml @@ -91,6 +91,12 @@ properties: it must be 1 if your system has audio_clkout0/1/2/3 enum: [0, 1]
+ "#address-cells": + const: 1 + + "#size-cells": + const: 0 + clock-frequency: description: for audio_clkout0/1/2/3
@@ -117,13 +123,7 @@ properties: description: List of necessary clock names. # details are defined below
- ports: - $ref: audio-graph-port.yaml#/definitions/port-base - unevaluatedProperties: false - patternProperties: - '^port(@[0-9a-f]+)?$': - $ref: "#/definitions/port-def" - + # ports is below port: $ref: "#/definitions/port-def"
@@ -242,8 +242,9 @@ properties: - interrupts additionalProperties: false
+patternProperties: # For DAI base - rcar_sound,dai: + 'rcar_sound,dai(@[0-9a-f]+)?$': description: DAI subnode. type: object patternProperties: @@ -263,6 +264,13 @@ properties: - capture additionalProperties: false
+ 'ports(@[0-9a-f]+)?$': + $ref: audio-graph-port.yaml#/definitions/port-base + unevaluatedProperties: false + patternProperties: + '^port(@[0-9a-f]+)?$': + $ref: "#/definitions/port-def" + required: - compatible - reg
On Mon, Jul 10, 2023 at 10:21:58AM +0900, Kuninori Morimoto wrote:
To enable multi Component support, "multi ports" is needed for Audio Graph Card/Card2, and "multi rcar_sound,dai" is needed for Simple Audio Card. This patch enable these.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
.../bindings/sound/renesas,rsnd.yaml | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml index d9808b130e8d..13a5a0a10fe6 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml @@ -91,6 +91,12 @@ properties: it must be 1 if your system has audio_clkout0/1/2/3 enum: [0, 1]
- "#address-cells":
- const: 1
- "#size-cells":
- const: 0
- clock-frequency: description: for audio_clkout0/1/2/3
@@ -117,13 +123,7 @@ properties: description: List of necessary clock names. # details are defined below
- ports:
- $ref: audio-graph-port.yaml#/definitions/port-base
- unevaluatedProperties: false
- patternProperties:
'^port(@[0-9a-f]+)?$':
$ref: "#/definitions/port-def"
- # ports is below port: $ref: "#/definitions/port-def"
@@ -242,8 +242,9 @@ properties: - interrupts additionalProperties: false
+patternProperties: # For DAI base
- rcar_sound,dai:
- 'rcar_sound,dai(@[0-9a-f]+)?$':
With a unit-address you need a 'reg' property.
description: DAI subnode. type: object patternProperties:
@@ -263,6 +264,13 @@ properties: - capture additionalProperties: false
- 'ports(@[0-9a-f]+)?$':
Multiple 'ports' nodes is not valid.
Here too would need a 'reg' property. Did you test any of this because that would be a dtc warning.
- $ref: audio-graph-port.yaml#/definitions/port-base
- unevaluatedProperties: false
- patternProperties:
'^port(@[0-9a-f]+)?$':
$ref: "#/definitions/port-def"
required:
- compatible
- reg
-- 2.25.1
Hi Rob
Thank you for reviewing
+patternProperties: # For DAI base
- rcar_sound,dai:
- 'rcar_sound,dai(@[0-9a-f]+)?$':
With a unit-address you need a 'reg' property.
(snip)
Here too would need a 'reg' property. Did you test any of this because that would be a dtc warning.
I don't remember detail, but some reviewer indicated that it is not needed, because we have default "reg" property somewhere/somehow.
Thank you for your help !!
Best regards --- Kuninori Morimoto
On Mon, 10 Jul 2023 10:19:04 +0900, Kuninori Morimoto wrote:
These are v2 of multi Component support.
On below HW case, we would like to use it as "2 Cards", but unfortunately it is impossible in intuitive way, or possible but not intuitive way. In reality, it is handled as "1 big Card" today.
[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[01/15] ASoC: soc-core: protect dlc->of_node under mutex commit: 065aa861b1243704b329a6d8407d8399614df6bd [02/15] ASoC: soc-core.c: initialize dlc on snd_soc_get_dai_id() commit: 521d675d2497f890e881dc48e954a1559460e97c [03/15] ASoC: soc-core.c: cleanup soc_dai_link_sanity_check() commit: 0e66a2c694096abc54ed58b3be654103f155ea43 [04/15] ASoC: soc-dai.c: add DAI get/match functions (no commit info) [05/15] ASoC: soc-core.c: enable multi Component (no commit info) [06/15] ASoC: soc-core.c: add snd_soc_get_dai_via_args() (no commit info) [07/15] ASoC: soc-core.c: add snd_soc_dlc_use_cpu_as_platform() (no commit info) [08/15] ASoC: soc-core.c: add snd_soc_copy_dai_args() (no commit info) [09/15] ASoC: simple-card-utils.c: enable multi Component support (no commit info) [10/15] ASoC: simple-card.c: enable multi Component support (no commit info)
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
participants (3)
-
Kuninori Morimoto
-
Mark Brown
-
Rob Herring