[alsa-devel] [PATCH v4 0/9] ASoC: add OF-graph base simple-card
Hi Rob, Mark
These are v4 of OF-graph base audio card patch-set. Main difference from v3 is that it doesn't have prefix on DT, and have new "label", "dai-format".
1) - 3) : new OF-graph helper functions 4) - 5) : expand "label", "dai-format" 6) - 9) : OF-graph base simple-card
Kuninori Morimoto (9): 1) of_graph: add of_graph_get_remote_endpoint() 2) of_graph: add of_graph_get_port_parent() 3) of_graph: add of_graph_get_endpoint_count() 4) ASoC: simple-card-utils: enable "label" on asoc_simple_card_parse_card_name 5) ASoC: soc-core: enable "dai-name" on snd_soc_of_parse_daifmt() 6) ASoC: add snd_soc_get_dai_id() 7) ASoC: simple-card-utils: add asoc_simple_card_parse_graph_dai() 8) ASoC: add audio-graph-card document 9) ASoC: add audio-graph-card support
.../devicetree/bindings/sound/audio-graph-card.txt | 124 +++++++++ .../devicetree/bindings/sound/simple-card.txt | 3 +- .../devicetree/bindings/sound/simple-scu-card.txt | 4 +- drivers/of/base.c | 60 +++- include/linux/of_graph.h | 21 ++ include/sound/simple_card_utils.h | 10 + include/sound/soc.h | 1 + sound/soc/generic/Kconfig | 8 + sound/soc/generic/Makefile | 2 + sound/soc/generic/audio-graph-card.c | 301 +++++++++++++++++++++ sound/soc/generic/simple-card-utils.c | 54 +++- sound/soc/soc-core.c | 32 ++- 12 files changed, 602 insertions(+), 18 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/audio-graph-card.txt create mode 100644 sound/soc/generic/audio-graph-card.c
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
It should use same method to get same result. To getting remote-endpoint node, let's use of_graph_get_remote_endpoint()
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v3 -> v4
- no change
drivers/of/base.c | 18 ++++++++++++++++-- include/linux/of_graph.h | 8 ++++++++ 2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/drivers/of/base.c b/drivers/of/base.c index d7c4629..3fad47f 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2455,6 +2455,20 @@ struct device_node *of_graph_get_endpoint_by_regs( EXPORT_SYMBOL(of_graph_get_endpoint_by_regs);
/** + * of_graph_get_remote_endpoint() - get remote endpoint node + * @node: pointer to a local endpoint device_node + * + * Return: Remote endpoint node associated with remote endpoint node linked + * to @node. Use of_node_put() on it when done. + */ +struct device_node *of_graph_get_remote_endpoint(const struct device_node *node) +{ + /* Get remote endpoint node. */ + return of_parse_phandle(node, "remote-endpoint", 0); +} +EXPORT_SYMBOL(of_graph_get_remote_endpoint); + +/** * of_graph_get_remote_port_parent() - get remote port's parent node * @node: pointer to a local endpoint device_node * @@ -2468,7 +2482,7 @@ struct device_node *of_graph_get_remote_port_parent( unsigned int depth;
/* Get remote endpoint node. */ - np = of_parse_phandle(node, "remote-endpoint", 0); + np = of_graph_get_remote_endpoint(node);
/* Walk 3 levels up only if there is 'ports' node. */ for (depth = 3; depth && np; depth--) { @@ -2492,7 +2506,7 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node) struct device_node *np;
/* Get remote endpoint node. */ - np = of_parse_phandle(node, "remote-endpoint", 0); + np = of_graph_get_remote_endpoint(node); if (!np) return NULL; return of_get_next_parent(np); diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h index abdb02e..0c9473a 100644 --- a/include/linux/of_graph.h +++ b/include/linux/of_graph.h @@ -48,6 +48,8 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, struct device_node *previous); struct device_node *of_graph_get_endpoint_by_regs( const struct device_node *parent, int port_reg, int reg); +struct device_node *of_graph_get_remote_endpoint( + const struct device_node *node); struct device_node *of_graph_get_remote_port_parent( const struct device_node *node); struct device_node *of_graph_get_remote_port(const struct device_node *node); @@ -80,6 +82,12 @@ static inline struct device_node *of_graph_get_endpoint_by_regs( return NULL; }
+static inline struct device_node *of_graph_get_remote_endpoint( + const struct device_node *node) +{ + return NULL; +} + static inline struct device_node *of_graph_get_remote_port_parent( const struct device_node *node) {
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Linux kernel already has of_graph_get_remote_port_parent(), but, sometimes we want to get own port parent. This patch adds of_graph_get_port_parent()
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v3 -> v4
- no change
drivers/of/base.c | 30 ++++++++++++++++++++++-------- include/linux/of_graph.h | 7 +++++++ 2 files changed, 29 insertions(+), 8 deletions(-)
diff --git a/drivers/of/base.c b/drivers/of/base.c index 3fad47f..eac37014 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2469,6 +2469,27 @@ struct device_node *of_graph_get_remote_endpoint(const struct device_node *node) EXPORT_SYMBOL(of_graph_get_remote_endpoint);
/** + * of_graph_get_port_parent() - get port's parent node + * @node: pointer to a local endpoint device_node + * + * Return: device node associated with endpoint node linked + * to @node. Use of_node_put() on it when done. + */ +struct device_node *of_graph_get_port_parent(struct device_node *node) +{ + unsigned int depth; + + /* Walk 3 levels up only if there is 'ports' node. */ + for (depth = 3; depth && node; depth--) { + node = of_get_next_parent(node); + if (depth == 2 && of_node_cmp(node->name, "ports")) + break; + } + return node; +} +EXPORT_SYMBOL(of_graph_get_port_parent); + +/** * of_graph_get_remote_port_parent() - get remote port's parent node * @node: pointer to a local endpoint device_node * @@ -2479,18 +2500,11 @@ struct device_node *of_graph_get_remote_port_parent( const struct device_node *node) { struct device_node *np; - unsigned int depth;
/* Get remote endpoint node. */ np = of_graph_get_remote_endpoint(node);
- /* Walk 3 levels up only if there is 'ports' node. */ - for (depth = 3; depth && np; depth--) { - np = of_get_next_parent(np); - if (depth == 2 && of_node_cmp(np->name, "ports")) - break; - } - return np; + return of_graph_get_port_parent(np); } EXPORT_SYMBOL(of_graph_get_remote_port_parent);
diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h index 0c9473a..9db632d 100644 --- a/include/linux/of_graph.h +++ b/include/linux/of_graph.h @@ -50,6 +50,7 @@ struct device_node *of_graph_get_endpoint_by_regs( const struct device_node *parent, int port_reg, int reg); struct device_node *of_graph_get_remote_endpoint( const struct device_node *node); +struct device_node *of_graph_get_port_parent(struct device_node *node); struct device_node *of_graph_get_remote_port_parent( const struct device_node *node); struct device_node *of_graph_get_remote_port(const struct device_node *node); @@ -88,6 +89,12 @@ static inline struct device_node *of_graph_get_remote_endpoint( return NULL; }
+static inline struct device_node *of_graph_get_port_parent( + struct device_node *node) +{ + return NULL; +} + static inline struct device_node *of_graph_get_remote_port_parent( const struct device_node *node) {
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
OF graph want to count its endpoint number, same as of_get_child_count(). This patch adds of_graph_get_endpoint_count()
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v3 -> v4
- no change
drivers/of/base.c | 12 ++++++++++++ include/linux/of_graph.h | 6 ++++++ 2 files changed, 18 insertions(+)
diff --git a/drivers/of/base.c b/drivers/of/base.c index eac37014..812edb9 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2527,6 +2527,18 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node) } EXPORT_SYMBOL(of_graph_get_remote_port);
+int of_graph_get_endpoint_count(const struct device_node *np) +{ + struct device_node *endpoint; + int num = 0; + + for_each_endpoint_of_node(np, endpoint) + num++; + + return num; +} +EXPORT_SYMBOL(of_graph_get_endpoint_count); + /** * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint * @node: pointer to parent device_node containing graph port/endpoint diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h index 9db632d..3e058f0 100644 --- a/include/linux/of_graph.h +++ b/include/linux/of_graph.h @@ -43,6 +43,7 @@ struct of_endpoint { #ifdef CONFIG_OF int of_graph_parse_endpoint(const struct device_node *node, struct of_endpoint *endpoint); +int of_graph_get_endpoint_count(const struct device_node *np); struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id); struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, struct device_node *previous); @@ -64,6 +65,11 @@ static inline int of_graph_parse_endpoint(const struct device_node *node, return -ENOSYS; }
+static inline int of_graph_get_endpoint_count(const struct device_node *np) +{ + return 0; +} + static inline struct device_node *of_graph_get_port_by_id( struct device_node *node, u32 id) {
The patch
of_graph: add of_graph_get_endpoint_count()
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
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
From ac1e6958d3be29a28889b09e4eec1798eccc1606 Mon Sep 17 00:00:00 2001
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com Date: Thu, 20 Apr 2017 01:32:47 +0000 Subject: [PATCH] of_graph: add of_graph_get_endpoint_count()
OF graph want to count its endpoint number, same as of_get_child_count(). This patch adds of_graph_get_endpoint_count()
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com Acked-by: Rob Herring robh@kernel.org Signed-off-by: Mark Brown broonie@kernel.org --- drivers/of/base.c | 12 ++++++++++++ include/linux/of_graph.h | 6 ++++++ 2 files changed, 18 insertions(+)
diff --git a/drivers/of/base.c b/drivers/of/base.c index 4c305599a664..cb1c49ae3b88 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2559,6 +2559,18 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node) } EXPORT_SYMBOL(of_graph_get_remote_port);
+int of_graph_get_endpoint_count(const struct device_node *np) +{ + struct device_node *endpoint; + int num = 0; + + for_each_endpoint_of_node(np, endpoint) + num++; + + return num; +} +EXPORT_SYMBOL(of_graph_get_endpoint_count); + /** * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint * @node: pointer to parent device_node containing graph port/endpoint diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h index 9db632d16cbc..3e058f05ab04 100644 --- a/include/linux/of_graph.h +++ b/include/linux/of_graph.h @@ -43,6 +43,7 @@ struct of_endpoint { #ifdef CONFIG_OF int of_graph_parse_endpoint(const struct device_node *node, struct of_endpoint *endpoint); +int of_graph_get_endpoint_count(const struct device_node *np); struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id); struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, struct device_node *previous); @@ -64,6 +65,11 @@ static inline int of_graph_parse_endpoint(const struct device_node *node, return -ENOSYS; }
+static inline int of_graph_get_endpoint_count(const struct device_node *np) +{ + return 0; +} + static inline struct device_node *of_graph_get_port_by_id( struct device_node *node, u32 id) {
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current asoc_simple_card_parse_card_name() detect [prefix]name, but in generally, we uses "label" for user visible names. This patch expand it to [prefix]label. This patch also update simple-xx-card.txt which is using this feature
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v3 -> v4
- new patch to expand name/labal
.../devicetree/bindings/sound/simple-card.txt | 1 + .../devicetree/bindings/sound/simple-scu-card.txt | 1 + sound/soc/generic/simple-card-utils.c | 18 ++++++++++++++---- 3 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt index c7a9393..f34a810 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-card.txt @@ -8,6 +8,7 @@ Required properties:
Optional properties:
+- simple-audio-card,label : see simple-audio-card,name - simple-audio-card,name : User specified audio sound card name, one string property. - simple-audio-card,widgets : Please refer to widgets.txt. diff --git a/Documentation/devicetree/bindings/sound/simple-scu-card.txt b/Documentation/devicetree/bindings/sound/simple-scu-card.txt index d6fe47e..01eb56e 100644 --- a/Documentation/devicetree/bindings/sound/simple-scu-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-scu-card.txt @@ -9,6 +9,7 @@ Required properties:
Optional properties:
+- simple-audio-card,label : see simple-audio-card,name - simple-audio-card,name : User specified audio sound card name, one string property. - simple-audio-card,cpu : CPU sub-node diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 4924575..4dfd9a2 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -82,14 +82,24 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card, char *prefix) { char prop[128]; + char *names[] = { + "label", "name" + }; + int i; int ret;
- snprintf(prop, sizeof(prop), "%sname", prefix); + if (!prefix) + prefix = "";
/* Parse the card name from DT */ - ret = snd_soc_of_parse_card_name(card, prop); - if (ret < 0) - return ret; + for (i = 0; i < ARRAY_SIZE(names); i++) { + snprintf(prop, sizeof(prop), "%s%s", prefix, names[i]); + ret = snd_soc_of_parse_card_name(card, prop); + if (ret < 0) + return ret; + if (card->name) + break; + }
if (!card->name && card->dai_link) card->name = card->dai_link->name;
On Mon, Mar 13, 2017 at 05:53:12AM +0000, Kuninori Morimoto wrote:
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current asoc_simple_card_parse_card_name() detect [prefix]name, but in generally, we uses "label" for user visible names. This patch expand it to [prefix]label. This patch also update simple-xx-card.txt which is using this feature
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
v3 -> v4
- new patch to expand name/labal
.../devicetree/bindings/sound/simple-card.txt | 1 + .../devicetree/bindings/sound/simple-scu-card.txt | 1 + sound/soc/generic/simple-card-utils.c | 18 ++++++++++++++---- 3 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt index c7a9393..f34a810 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-card.txt @@ -8,6 +8,7 @@ Required properties:
Optional properties:
+- simple-audio-card,label : see simple-audio-card,name
Just "label". That's a standard property name.
- simple-audio-card,name : User specified audio sound card name, one string property.
- simple-audio-card,widgets : Please refer to widgets.txt.
diff --git a/Documentation/devicetree/bindings/sound/simple-scu-card.txt b/Documentation/devicetree/bindings/sound/simple-scu-card.txt index d6fe47e..01eb56e 100644 --- a/Documentation/devicetree/bindings/sound/simple-scu-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-scu-card.txt @@ -9,6 +9,7 @@ Required properties:
Optional properties:
+- simple-audio-card,label : see simple-audio-card,name
Ditto.
- simple-audio-card,name : User specified audio sound card name, one string property.
- simple-audio-card,cpu : CPU sub-node
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current snd_soc_of_parse_daifmt() detects [prefix]format, but "format" was unclear. This patch expand it to [prefix]dai-format, too This patch update simple-xx-card.txt which uses this feature
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v3 -> v4
- expand format
Documentation/devicetree/bindings/sound/simple-card.txt | 2 +- Documentation/devicetree/bindings/sound/simple-scu-card.txt | 3 ++- sound/soc/soc-core.c | 9 +++++++-- 3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt index f34a810..6e8a8a3 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-card.txt @@ -48,7 +48,7 @@ Required dai-link subnodes:
Optional dai-link subnode properties:
-- format : CPU/CODEC common audio format. +- dai-format / format : CPU/CODEC common audio format. "i2s", "right_j", "left_j" , "dsp_a" "dsp_b", "ac97", "pdm", "msb", "lsb" - frame-master : Indicates dai-link frame master. diff --git a/Documentation/devicetree/bindings/sound/simple-scu-card.txt b/Documentation/devicetree/bindings/sound/simple-scu-card.txt index 01eb56e..47b4677 100644 --- a/Documentation/devicetree/bindings/sound/simple-scu-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-scu-card.txt @@ -17,9 +17,10 @@ Optional properties:
Optional subnode properties:
-- simple-audio-card,format : CPU/CODEC common audio format. +- simple-audio-card,dai-format : CPU/CODEC common audio format. "i2s", "right_j", "left_j" , "dsp_a" "dsp_b", "ac97", "pdm", "msb", "lsb" +- simple-audio-card,format : see simple-audio-card,dai-format - simple-audio-card,frame-master : Indicates dai-link frame master. phandle to a cpu or codec subnode. - simple-audio-card,bitclock-master : Indicates dai-link bit clock master. diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6dca408..175ade0 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3951,11 +3951,16 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, prefix = "";
/* - * check "[prefix]format = xxx" + * check "[prefix]dai-format = xxx" + * or "[prefix]format = xxx" * SND_SOC_DAIFMT_FORMAT_MASK area */ - snprintf(prop, sizeof(prop), "%sformat", prefix); + snprintf(prop, sizeof(prop), "%sdai-format", prefix); ret = of_property_read_string(np, prop, &str); + if (ret < 0) { + snprintf(prop, sizeof(prop), "%sformat", prefix); + ret = of_property_read_string(np, prop, &str); + } if (ret == 0) { for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) { if (strcmp(str, of_fmt_table[i].name) == 0) {
On Mon, Mar 13, 2017 at 05:53:30AM +0000, Kuninori Morimoto wrote:
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current snd_soc_of_parse_daifmt() detects [prefix]format, but "format" was unclear. This patch expand it to [prefix]dai-format, too This patch update simple-xx-card.txt which uses this feature
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
v3 -> v4
- expand format
Documentation/devicetree/bindings/sound/simple-card.txt | 2 +- Documentation/devicetree/bindings/sound/simple-scu-card.txt | 3 ++- sound/soc/soc-core.c | 9 +++++++-- 3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt index f34a810..6e8a8a3 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-card.txt @@ -48,7 +48,7 @@ Required dai-link subnodes:
Optional dai-link subnode properties:
-- format : CPU/CODEC common audio format. +- dai-format / format : CPU/CODEC common audio format. "i2s", "right_j", "left_j" , "dsp_a" "dsp_b", "ac97", "pdm", "msb", "lsb"
- frame-master : Indicates dai-link frame master.
diff --git a/Documentation/devicetree/bindings/sound/simple-scu-card.txt b/Documentation/devicetree/bindings/sound/simple-scu-card.txt index 01eb56e..47b4677 100644 --- a/Documentation/devicetree/bindings/sound/simple-scu-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-scu-card.txt @@ -17,9 +17,10 @@ Optional properties:
Optional subnode properties:
-- simple-audio-card,format : CPU/CODEC common audio format. +- simple-audio-card,dai-format : CPU/CODEC common audio format.
This one should not be added.
"i2s", "right_j", "left_j" , "dsp_a" "dsp_b", "ac97", "pdm", "msb", "lsb"
+- simple-audio-card,format : see simple-audio-card,dai-format
- simple-audio-card,frame-master : Indicates dai-link frame master. phandle to a cpu or codec subnode.
- simple-audio-card,bitclock-master : Indicates dai-link bit clock master.
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
ALSA SoC needs to know connected DAI ID for probing. On OF-graph case, basically we can check DT port location.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v3 -> v4
- no change
include/sound/soc.h | 1 + sound/soc/soc-core.c | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+)
diff --git a/include/sound/soc.h b/include/sound/soc.h index cdfb55f..ab4639e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1664,6 +1664,7 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, const char *prefix, struct device_node **bitclkmaster, struct device_node **framemaster); +int snd_soc_get_dai_id(struct device_node *ep); int snd_soc_get_dai_name(struct of_phandle_args *args, const char **dai_name); int snd_soc_of_get_dai_name(struct device_node *of_node, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 175ade0..c91010d 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -34,6 +34,7 @@ #include <linux/ctype.h> #include <linux/slab.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <linux/dmi.h> #include <sound/core.h> #include <sound/jack.h> @@ -4040,6 +4041,28 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt);
+int snd_soc_get_dai_id(struct device_node *ep) +{ + struct device_node *node; + struct device_node *endpoint; + int i, id; + + node = of_graph_get_port_parent(ep); + + i = 0; + id = -1; + for_each_endpoint_of_node(node, endpoint) { + if (endpoint == ep) + id = i; + i++; + } + if (id < 0) + return -ENODEV; + + return id; +} +EXPORT_SYMBOL_GPL(snd_soc_get_dai_id); + int snd_soc_get_dai_name(struct of_phandle_args *args, const char **dai_name) {
On Mon, Mar 13, 2017 at 12:53 AM, Kuninori Morimoto kuninori.morimoto.gx@renesas.com wrote:
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
ALSA SoC needs to know connected DAI ID for probing. On OF-graph case, basically we can check DT port location.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
v3 -> v4
- no change
include/sound/soc.h | 1 + sound/soc/soc-core.c | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+)
diff --git a/include/sound/soc.h b/include/sound/soc.h index cdfb55f..ab4639e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1664,6 +1664,7 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, const char *prefix, struct device_node **bitclkmaster, struct device_node **framemaster); +int snd_soc_get_dai_id(struct device_node *ep); int snd_soc_get_dai_name(struct of_phandle_args *args, const char **dai_name); int snd_soc_of_get_dai_name(struct device_node *of_node, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 175ade0..c91010d 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -34,6 +34,7 @@ #include <linux/ctype.h> #include <linux/slab.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <linux/dmi.h> #include <sound/core.h> #include <sound/jack.h> @@ -4040,6 +4041,28 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt);
+int snd_soc_get_dai_id(struct device_node *ep)
Shouldn't the DAI id be the index of the "dais" property?
+{
struct device_node *node;
struct device_node *endpoint;
int i, id;
node = of_graph_get_port_parent(ep);
i = 0;
id = -1;
for_each_endpoint_of_node(node, endpoint) {
if (endpoint == ep)
id = i;
I don't see how this works when you have 1 DAI controller with multiple endpoints versus multiple DAI controllers with a single endpoint each. All the IDs will be 0 in the latter case.
i++;
}
if (id < 0)
return -ENODEV;
return id;
+} +EXPORT_SYMBOL_GPL(snd_soc_get_dai_id);
int snd_soc_get_dai_name(struct of_phandle_args *args, const char **dai_name) { -- 1.9.1
Hi Rob
Thank you for your review
+{
struct device_node *node;
struct device_node *endpoint;
int i, id;
node = of_graph_get_port_parent(ep);
i = 0;
id = -1;
for_each_endpoint_of_node(node, endpoint) {
if (endpoint == ep)
id = i;
I don't see how this works when you have 1 DAI controller with multiple endpoints versus multiple DAI controllers with a single endpoint each. All the IDs will be 0 in the latter case.
It support 1:1 endpoint pattern only.
Best regards --- Kuninori Morimoto
On Mon, Mar 20, 2017 at 8:59 PM, Kuninori Morimoto kuninori.morimoto.gx@renesas.com wrote:
Hi Rob
Thank you for your review
+{
struct device_node *node;
struct device_node *endpoint;
int i, id;
node = of_graph_get_port_parent(ep);
i = 0;
id = -1;
for_each_endpoint_of_node(node, endpoint) {
if (endpoint == ep)
id = i;
I don't see how this works when you have 1 DAI controller with multiple endpoints versus multiple DAI controllers with a single endpoint each. All the IDs will be 0 in the latter case.
It support 1:1 endpoint pattern only.
Then the endpoint id is always 0 and this function is pointless.
Rob
Hi Rob
Thank you for your review.
+{
struct device_node *node;
struct device_node *endpoint;
int i, id;
node = of_graph_get_port_parent(ep);
i = 0;
id = -1;
for_each_endpoint_of_node(node, endpoint) {
if (endpoint == ep)
id = i;
I don't see how this works when you have 1 DAI controller with multiple endpoints versus multiple DAI controllers with a single endpoint each. All the IDs will be 0 in the latter case.
It support 1:1 endpoint pattern only.
Then the endpoint id is always 0 and this function is pointless.
Sorry, I checked my patch-list, and I noticed that this function will be expand to use callback function in next patch-set (= HDMI support). Thus, inded current function is pointless at this point. I will merge this expansion patch in v5
Hi Rob, again
+{
struct device_node *node;
struct device_node *endpoint;
int i, id;
node = of_graph_get_port_parent(ep);
i = 0;
id = -1;
for_each_endpoint_of_node(node, endpoint) {
if (endpoint == ep)
id = i;
I don't see how this works when you have 1 DAI controller with multiple endpoints versus multiple DAI controllers with a single endpoint each. All the IDs will be 0 in the latter case.
It support 1:1 endpoint pattern only.
Then the endpoint id is always 0 and this function is pointless.
Sorry, I checked my patch-list, and I noticed that this function will be expand to use callback function in next patch-set (= HDMI support). Thus, inded current function is pointless at this point. I will merge this expansion patch in v5
1 correction.
sound graph might have multi ports (= not multi endpoint), like below. Each endpoints are 1:1.
ports { port { endpoint }; /* ID = 0 */ port { endpoint }; /* ID = 1 */ port { endpoint }; /* ID = 2 */ };
1 question
It will support HDMI sound feature, thus I separated it into OF-graph (= this patch-set) and HDMI (= next patch-set). Should I merge it ? Below is the expansion patch for HDMI support
---------------------- Subject: [PATCH 14/63] ASoC: add .of_xlate_dai_id() callback
ALSA SoC needs to know connected DAI ID for probing. It is not a big problem if device/driver was only for sound, but getting DAI ID will be difficult if device includes both Video/Sound, like HDMI. To solve this issue, this patch adds new .of_xlate_dai_id callback on component driver, and use it from snd_soc_get_dai_id()
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/soc.h | 3 +++ sound/soc/soc-core.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+)
diff --git a/include/sound/soc.h b/include/sound/soc.h index 95abbcb..0055fa0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -14,6 +14,7 @@ #define __LINUX_SND_SOC_H
#include <linux/of.h> +#include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/types.h> #include <linux/notifier.h> @@ -793,6 +794,8 @@ struct snd_soc_component_driver { int (*of_xlate_dai_name)(struct snd_soc_component *component, struct of_phandle_args *args, const char **dai_name); + int (*of_xlate_dai_id)(struct snd_soc_component *comment, + struct device_node *endpoint); void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type, int subseq); int (*stream_event)(struct snd_soc_component *, int event); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 7a10522..07e4eec 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4042,12 +4042,45 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
int snd_soc_get_dai_id(struct device_node *ep) { + struct snd_soc_component *pos; struct device_node *node; struct device_node *endpoint; int i, id; + int ret;
node = of_graph_get_port_parent(ep);
+ /* + * For example HDMI case, HDMI has video/sound port, + * but ALSA SoC needs sound port number only. + * Thus counting HDMI DT port/endpoint doesn't work. + * Then, it should have .of_xlate_dai_id + */ + ret = -ENOTSUPP; + mutex_lock(&client_mutex); + list_for_each_entry(pos, &component_list, list) { + struct device_node *component_of_node = pos->dev->of_node; + + if (!component_of_node && pos->dev->parent) + component_of_node = pos->dev->parent->of_node; + + if (component_of_node != node) + continue; + + if (pos->driver->of_xlate_dai_id) + ret = pos->driver->of_xlate_dai_id(pos, ep); + + break; + } + mutex_unlock(&client_mutex); + + if (ret != -ENOTSUPP) + return ret; + + /* + * Non HDMI sound case, counting port/endpoint on its DT + * is enough. Let's count it. + */ i = 0; id = -1; for_each_endpoint_of_node(node, endpoint) {
On Sun, Apr 9, 2017 at 7:45 PM, Kuninori Morimoto kuninori.morimoto.gx@renesas.com wrote:
Hi Rob, again
+{
struct device_node *node;
struct device_node *endpoint;
int i, id;
node = of_graph_get_port_parent(ep);
i = 0;
id = -1;
for_each_endpoint_of_node(node, endpoint) {
if (endpoint == ep)
id = i;
I don't see how this works when you have 1 DAI controller with multiple endpoints versus multiple DAI controllers with a single endpoint each. All the IDs will be 0 in the latter case.
It support 1:1 endpoint pattern only.
Then the endpoint id is always 0 and this function is pointless.
Sorry, I checked my patch-list, and I noticed that this function will be expand to use callback function in next patch-set (= HDMI support). Thus, inded current function is pointless at this point. I will merge this expansion patch in v5
1 correction.
sound graph might have multi ports (= not multi endpoint), like below. Each endpoints are 1:1.
ports { port { endpoint }; /* ID = 0 */
port@0
port { endpoint }; /* ID = 1 */
port@1, etc.
port { endpoint }; /* ID = 2 */
These ports are audio channels? If these are 3 separate data paths, then this is correct. If you have a single data path with multiple connections (e.g. a mux), then that should be a single port with multiple endpoints. For example, a design that routes the same I2S interface to HDMI and a codec and their use is mutually exclusive. I imagine you will need to support both.
The pattern I prefer to see calling graph functions is that drivers are specific about which port and endpoint number for a parent node they want. Not just searching the graph for any match.
};
1 question
It will support HDMI sound feature, thus I separated it into OF-graph (= this patch-set) and HDMI (= next patch-set). Should I merge it ?
I think so if it affects the functions here. It seems better to let the driver controlling the DAI determine the id mapping than trying to do it in the core.
Below is the expansion patch for HDMI support
Subject: [PATCH 14/63] ASoC: add .of_xlate_dai_id() callback
ALSA SoC needs to know connected DAI ID for probing. It is not a big problem if device/driver was only for sound, but getting DAI ID will be difficult if device includes both Video/Sound, like HDMI. To solve this issue, this patch adds new .of_xlate_dai_id callback on component driver, and use it from snd_soc_get_dai_id()
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
include/sound/soc.h | 3 +++ sound/soc/soc-core.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+)
diff --git a/include/sound/soc.h b/include/sound/soc.h index 95abbcb..0055fa0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -14,6 +14,7 @@ #define __LINUX_SND_SOC_H
#include <linux/of.h> +#include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/types.h> #include <linux/notifier.h> @@ -793,6 +794,8 @@ struct snd_soc_component_driver { int (*of_xlate_dai_name)(struct snd_soc_component *component, struct of_phandle_args *args, const char **dai_name);
int (*of_xlate_dai_id)(struct snd_soc_component *comment,
struct device_node *endpoint); void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type, int subseq); int (*stream_event)(struct snd_soc_component *, int event);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 7a10522..07e4eec 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4042,12 +4042,45 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
int snd_soc_get_dai_id(struct device_node *ep) {
struct snd_soc_component *pos; struct device_node *node; struct device_node *endpoint; int i, id;
int ret; node = of_graph_get_port_parent(ep);
/*
* For example HDMI case, HDMI has video/sound port,
* but ALSA SoC needs sound port number only.
* Thus counting HDMI DT port/endpoint doesn't work.
* Then, it should have .of_xlate_dai_id
Perhaps you should always require this function and provide a default implementation (or several).
*/
ret = -ENOTSUPP;
mutex_lock(&client_mutex);
list_for_each_entry(pos, &component_list, list) {
struct device_node *component_of_node = pos->dev->of_node;
if (!component_of_node && pos->dev->parent)
component_of_node = pos->dev->parent->of_node;
if (component_of_node != node)
continue;
if (pos->driver->of_xlate_dai_id)
ret = pos->driver->of_xlate_dai_id(pos, ep);
break;
}
mutex_unlock(&client_mutex);
if (ret != -ENOTSUPP)
return ret;
/*
* Non HDMI sound case, counting port/endpoint on its DT
* is enough. Let's count it.
*/ i = 0; id = -1; for_each_endpoint_of_node(node, endpoint) {
-- 1.9.1
Best regards
Kuninori Morimoto
Hi Rob
ports { port { endpoint }; /* ID = 0 */ port { endpoint }; /* ID = 1 */ port { endpoint }; /* ID = 2 */
These ports are audio channels? If these are 3 separate data paths, then this is correct. If you have a single data path with multiple connections (e.g. a mux), then that should be a single port with multiple endpoints. For example, a design that routes the same I2S interface to HDMI and a codec and their use is mutually exclusive. I imagine you will need to support both.
Thanks. Maybe we wil need it in the future, but not yet supported now.
The pattern I prefer to see calling graph functions is that drivers are specific about which port and endpoint number for a parent node they want. Not just searching the graph for any match.
This related feature will be needed on HDMI sound support, because it has not only sound port/endpoint.
1 question
It will support HDMI sound feature, thus I separated it into OF-graph (= this patch-set) and HDMI (= next patch-set). Should I merge it ?
I think so if it affects the functions here. It seems better to let the driver controlling the DAI determine the id mapping than trying to do it in the core.
At this point, this feature (= HDMI sound) is not needed for OF-graph patch-set. Thus, as-is is OK for OF-graph patch-set ?
Best regards --- Kuninori Morimoto
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
simple-card already has asoc_simple_card_parse_dai(), but graph base parsing needs graph specific version of it.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v3 -> v4
- no change
include/sound/simple_card_utils.h | 10 ++++++++++ sound/soc/generic/simple-card-utils.c | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index af58d23..efab584 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -60,6 +60,16 @@ int asoc_simple_card_parse_dai(struct device_node *node, const char *cells_name, int *is_single_links);
+#define asoc_simple_card_parse_graph_cpu(ep, dai_link) \ + asoc_simple_card_parse_graph_dai(ep, &dai_link->cpu_of_node, \ + &dai_link->cpu_dai_name) +#define asoc_simple_card_parse_graph_codec(ep, dai_link) \ + asoc_simple_card_parse_graph_dai(ep, &dai_link->codec_of_node, \ + &dai_link->codec_dai_name) +int asoc_simple_card_parse_graph_dai(struct device_node *ep, + struct device_node **endpoint_np, + const char **dai_name); + int asoc_simple_card_init_dai(struct snd_soc_dai *dai, struct asoc_simple_dai *simple_dai);
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 4dfd9a2..67c3fa4 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -10,6 +10,7 @@ #include <linux/clk.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <sound/simple_card_utils.h>
int asoc_simple_card_parse_daifmt(struct device *dev, @@ -174,6 +175,41 @@ int asoc_simple_card_parse_dai(struct device_node *node, } EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai);
+int asoc_simple_card_parse_graph_dai(struct device_node *ep, + struct device_node **dai_of_node, + const char **dai_name) +{ + struct device_node *node; + struct of_phandle_args args; + int ret; + + if (!ep) + return 0; + if (!dai_name) + return 0; + + /* + * of_graph_get_port_parent() will call + * of_node_put(). So, call of_node_get() here + */ + of_node_get(ep); + node = of_graph_get_port_parent(ep); + + /* Get dai->name */ + args.np = node; + args.args[0] = snd_soc_get_dai_id(ep); + args.args_count = (of_graph_get_endpoint_count(node) > 1); + + ret = snd_soc_get_dai_name(&args, dai_name); + if (ret < 0) + return ret; + + *dai_of_node = node; + + return 0; +} +EXPORT_SYMBOL_GPL(asoc_simple_card_parse_graph_dai); + int asoc_simple_card_init_dai(struct snd_soc_dai *dai, struct asoc_simple_dai *simple_dai) {
On Mon, Mar 13, 2017 at 12:54 AM, Kuninori Morimoto kuninori.morimoto.gx@renesas.com wrote:
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
simple-card already has asoc_simple_card_parse_dai(), but graph base parsing needs graph specific version of it.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
v3 -> v4
- no change
include/sound/simple_card_utils.h | 10 ++++++++++ sound/soc/generic/simple-card-utils.c | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index af58d23..efab584 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -60,6 +60,16 @@ int asoc_simple_card_parse_dai(struct device_node *node, const char *cells_name, int *is_single_links);
+#define asoc_simple_card_parse_graph_cpu(ep, dai_link) \
asoc_simple_card_parse_graph_dai(ep, &dai_link->cpu_of_node, \
&dai_link->cpu_dai_name)
+#define asoc_simple_card_parse_graph_codec(ep, dai_link) \
asoc_simple_card_parse_graph_dai(ep, &dai_link->codec_of_node, \
&dai_link->codec_dai_name)
+int asoc_simple_card_parse_graph_dai(struct device_node *ep,
struct device_node **endpoint_np,
const char **dai_name);
int asoc_simple_card_init_dai(struct snd_soc_dai *dai, struct asoc_simple_dai *simple_dai);
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 4dfd9a2..67c3fa4 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -10,6 +10,7 @@ #include <linux/clk.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <sound/simple_card_utils.h>
int asoc_simple_card_parse_daifmt(struct device *dev, @@ -174,6 +175,41 @@ int asoc_simple_card_parse_dai(struct device_node *node, } EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai);
+int asoc_simple_card_parse_graph_dai(struct device_node *ep,
struct device_node **dai_of_node,
const char **dai_name)
+{
struct device_node *node;
struct of_phandle_args args;
int ret;
if (!ep)
return 0;
if (!dai_name)
return 0;
/*
* of_graph_get_port_parent() will call
* of_node_put(). So, call of_node_get() here
*/
of_node_get(ep);
node = of_graph_get_port_parent(ep);
/* Get dai->name */
args.np = node;
args.args[0] = snd_soc_get_dai_id(ep);
args.args_count = (of_graph_get_endpoint_count(node) > 1);
This is a bit pointless. It's not the kernel's job to validate the DT. If you don't have a graph, then of_graph_get_port_parent would have already failed.
ret = snd_soc_get_dai_name(&args, dai_name);
if (ret < 0)
return ret;
*dai_of_node = node;
return 0;
+} +EXPORT_SYMBOL_GPL(asoc_simple_card_parse_graph_dai);
int asoc_simple_card_init_dai(struct snd_soc_dai *dai, struct asoc_simple_dai *simple_dai) { -- 1.9.1
Hi Rob
Thank you for your review.
+int asoc_simple_card_parse_graph_dai(struct device_node *ep,
struct device_node **dai_of_node,
const char **dai_name)
+{
struct device_node *node;
struct of_phandle_args args;
int ret;
if (!ep)
return 0;
if (!dai_name)
return 0;
/*
* of_graph_get_port_parent() will call
* of_node_put(). So, call of_node_get() here
*/
of_node_get(ep);
node = of_graph_get_port_parent(ep);
/* Get dai->name */
args.np = node;
args.args[0] = snd_soc_get_dai_id(ep);
args.args_count = (of_graph_get_endpoint_count(node) > 1);
This is a bit pointless. It's not the kernel's job to validate the DT. If you don't have a graph, then of_graph_get_port_parent would have already failed.
This function will be called from graph base driver, thus, it should have port parent. args.args_count is checking <&xxxx> or <&xxxx n>
Best regards --- Kuninori Morimoto
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
"Audio Graph Card" = "Simple Card" + "OF-graph"
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v3 -> v4
- remove prefix - asoc-audio-graph-card -> audio-graph-card - dai-controller -> audio-codec
.../devicetree/bindings/sound/audio-graph-card.txt | 124 +++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/audio-graph-card.txt
diff --git a/Documentation/devicetree/bindings/sound/audio-graph-card.txt b/Documentation/devicetree/bindings/sound/audio-graph-card.txt new file mode 100644 index 0000000..bac4b1b --- /dev/null +++ b/Documentation/devicetree/bindings/sound/audio-graph-card.txt @@ -0,0 +1,124 @@ +Audio Graph Card: + +Audio Graph Card specifies audio DAI connections of SoC <-> codec. +It is based on common bindings for device graphs. +see ${LINUX}/Documentation/devicetree/bindings/graph.txt + +Basically, Audio Graph Card property is same as Simple Card. +see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt + +Below are same as Simple-Card. + +- label +- dai-format +- frame-master +- bitclock-master +- bitclock-inversion +- frame-inversion +- dai-tdm-slot-num +- dai-tdm-slot-width +- clocks / system-clock-frequency + +Required properties: + +- compatible : "audio-graph-card"; +- dais : list of CPU DAI port{s} + +Example: Single DAI case + + sound_card { + compatible = "audio-graph-card"; + + dais = <&cpu_port>; + }; + + dai-controller { + ... + cpu_port: port { + cpu_endpoint: endpoint { + remote-endpoint = <&codec_endpoint>; + + dai-format = "left_j"; + ... + }; + }; + }; + + audio-codec { + ... + port { + codec_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint>; + }; + }; + }; + +Example: Multi DAI case + + sound-card { + compatible = "audio-graph-card"; + + label = "sound-card"; + + dais = <&cpu_port0 + &cpu_port1 + &cpu_port2>; + }; + + audio-codec@0 { + ... + port { + codec0_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint0>; + }; + }; + }; + + audio-codec@1 { + ... + port { + codec1_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint1>; + }; + }; + }; + + audio-codec@2 { + ... + port { + codec2_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint2>; + }; + }; + }; + + dai-controller { + ... + ports { + cpu_port0: port@0 { + cpu_endpoint0: endpoint { + remote-endpoint = <&codec0_endpoint>; + + dai-format = "left_j"; + ... + }; + }; + cpu_port1: port@1 { + cpu_endpoint1: endpoint { + remote-endpoint = <&codec1_endpoint>; + + dai-format = "i2s"; + ... + }; + }; + cpu_port2: port@2 { + cpu_endpoint2: endpoint { + remote-endpoint = <&codec2_endpoint>; + + dai-format = "i2s"; + ... + }; + }; + }; + }; +
On Mon, Mar 13, 2017 at 05:54:22AM +0000, Kuninori Morimoto wrote:
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
"Audio Graph Card" = "Simple Card" + "OF-graph"
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
v3 -> v4
- remove prefix
- asoc-audio-graph-card -> audio-graph-card
- dai-controller -> audio-codec
.../devicetree/bindings/sound/audio-graph-card.txt | 124 +++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/audio-graph-card.txt
Reviewed-by: Rob Herring robh@kernel.org
The patch
ASoC: add audio-graph-card document
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
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
From 2d4e31de5bb2b5fbdbcd8a3bfec0eae0bd4ca409 Mon Sep 17 00:00:00 2001
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com Date: Thu, 20 Apr 2017 01:35:41 +0000 Subject: [PATCH] ASoC: add audio-graph-card document
"Audio Graph Card" = "Simple Card" + "OF-graph"
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com Reviewed-by: Rob Herring robh@kernel.org Signed-off-by: Mark Brown broonie@kernel.org --- .../devicetree/bindings/sound/audio-graph-card.txt | 124 +++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/audio-graph-card.txt
diff --git a/Documentation/devicetree/bindings/sound/audio-graph-card.txt b/Documentation/devicetree/bindings/sound/audio-graph-card.txt new file mode 100644 index 000000000000..bac4b1b1060f --- /dev/null +++ b/Documentation/devicetree/bindings/sound/audio-graph-card.txt @@ -0,0 +1,124 @@ +Audio Graph Card: + +Audio Graph Card specifies audio DAI connections of SoC <-> codec. +It is based on common bindings for device graphs. +see ${LINUX}/Documentation/devicetree/bindings/graph.txt + +Basically, Audio Graph Card property is same as Simple Card. +see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt + +Below are same as Simple-Card. + +- label +- dai-format +- frame-master +- bitclock-master +- bitclock-inversion +- frame-inversion +- dai-tdm-slot-num +- dai-tdm-slot-width +- clocks / system-clock-frequency + +Required properties: + +- compatible : "audio-graph-card"; +- dais : list of CPU DAI port{s} + +Example: Single DAI case + + sound_card { + compatible = "audio-graph-card"; + + dais = <&cpu_port>; + }; + + dai-controller { + ... + cpu_port: port { + cpu_endpoint: endpoint { + remote-endpoint = <&codec_endpoint>; + + dai-format = "left_j"; + ... + }; + }; + }; + + audio-codec { + ... + port { + codec_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint>; + }; + }; + }; + +Example: Multi DAI case + + sound-card { + compatible = "audio-graph-card"; + + label = "sound-card"; + + dais = <&cpu_port0 + &cpu_port1 + &cpu_port2>; + }; + + audio-codec@0 { + ... + port { + codec0_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint0>; + }; + }; + }; + + audio-codec@1 { + ... + port { + codec1_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint1>; + }; + }; + }; + + audio-codec@2 { + ... + port { + codec2_endpoint: endpoint { + remote-endpoint = <&cpu_endpoint2>; + }; + }; + }; + + dai-controller { + ... + ports { + cpu_port0: port@0 { + cpu_endpoint0: endpoint { + remote-endpoint = <&codec0_endpoint>; + + dai-format = "left_j"; + ... + }; + }; + cpu_port1: port@1 { + cpu_endpoint1: endpoint { + remote-endpoint = <&codec1_endpoint>; + + dai-format = "i2s"; + ... + }; + }; + cpu_port2: port@2 { + cpu_endpoint2: endpoint { + remote-endpoint = <&codec2_endpoint>; + + dai-format = "i2s"; + ... + }; + }; + }; + }; +
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
OF-graph base DT binding are used on V4L2, and ALSA SoC is using different style of DT today. Now ALSA SoC supports simple-card driver for generic/simple sound card. In the future, V4L2 / ALSA will support HDMI, and then, DT bindings between V4L2 / ALSA should be merged. This patch adds new Audio Graph Card which is OF-graph base of simple-card
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v3 -> v4
- use "label" - use "dai-format" - don't use prefix
sound/soc/generic/Kconfig | 8 + sound/soc/generic/Makefile | 2 + sound/soc/generic/audio-graph-card.c | 301 +++++++++++++++++++++++++++++++++++ 3 files changed, 311 insertions(+) create mode 100644 sound/soc/generic/audio-graph-card.c
diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index d023959..121a48e 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -14,3 +14,11 @@ config SND_SIMPLE_SCU_CARD help This option enables generic simple SCU sound card support. It supports DPCM of multi CPU single Codec system. + +config SND_AUDIO_GRAPH_CARD + tristate "ASoC Audio Graph sound card support" + depends on OF + select SND_SIMPLE_CARD_UTILS + help + This option enables generic simple simple sound card support + with OF-graph DT bindings. diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index ee750f3..670068f 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -1,7 +1,9 @@ snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-objs := simple-card.o snd-soc-simple-scu-card-objs := simple-scu-card.o +snd-soc-audio-graph-card-objs := audio-graph-card.o
obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o +obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c new file mode 100644 index 0000000..07afdfa --- /dev/null +++ b/sound/soc/generic/audio-graph-card.c @@ -0,0 +1,301 @@ +/* + * ASoC audio graph sound card support + * + * Copyright (C) 2016 Renesas Solutions Corp. + * Kuninori Morimoto kuninori.morimoto.gx@renesas.com + * + * based on ${LINUX}/sound/soc/generic/simple-card.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/string.h> +#include <sound/jack.h> +#include <sound/simple_card_utils.h> + +struct graph_card_data { + struct snd_soc_card snd_card; + struct graph_dai_props { + struct asoc_simple_dai cpu_dai; + struct asoc_simple_dai codec_dai; + } *dai_props; + struct snd_soc_dai_link *dai_link; +}; + +#define graph_priv_to_dev(priv) ((priv)->snd_card.dev) +#define graph_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) +#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i)) + +static int asoc_graph_card_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_dai_props *dai_props = + graph_priv_to_props(priv, rtd->num); + int ret; + + ret = clk_prepare_enable(dai_props->cpu_dai.clk); + if (ret) + return ret; + + ret = clk_prepare_enable(dai_props->codec_dai.clk); + if (ret) + clk_disable_unprepare(dai_props->cpu_dai.clk); + + return ret; +} + +static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_dai_props *dai_props = + graph_priv_to_props(priv, rtd->num); + + clk_disable_unprepare(dai_props->cpu_dai.clk); + + clk_disable_unprepare(dai_props->codec_dai.clk); +} + +static struct snd_soc_ops asoc_graph_card_ops = { + .startup = asoc_graph_card_startup, + .shutdown = asoc_graph_card_shutdown, +}; + +static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; + struct graph_dai_props *dai_props = + graph_priv_to_props(priv, rtd->num); + int ret; + + ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai); + if (ret < 0) + return ret; + + ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai); + if (ret < 0) + return ret; + + return 0; +} + +static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, + struct graph_card_data *priv, + int idx) +{ + struct device *dev = graph_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx); + struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; + struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; + struct device_node *cpu_ep = of_get_next_child(cpu_port, NULL); + struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep); + struct device_node *rcpu_ep = of_graph_get_remote_endpoint(codec_ep); + int ret; + + if (rcpu_ep != cpu_ep) { + dev_err(dev, "remote-endpoint missmatch (%s/%s/%s)\n", + cpu_ep->name, codec_ep->name, rcpu_ep->name); + return -EINVAL; + } + + ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, + NULL, &dai_link->dai_fmt); + if (ret < 0) + goto dai_link_of_err; + + /* + * we need to consider "mclk-fs" around here + * see simple-card + */ + + ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link); + if (ret < 0) + goto dai_link_of_err; + + ret = snd_soc_of_parse_tdm_slot(cpu_ep, + &cpu_dai->tx_slot_mask, + &cpu_dai->rx_slot_mask, + &cpu_dai->slots, + &cpu_dai->slot_width); + if (ret < 0) + goto dai_link_of_err; + + ret = snd_soc_of_parse_tdm_slot(codec_ep, + &codec_dai->tx_slot_mask, + &codec_dai->rx_slot_mask, + &codec_dai->slots, + &codec_dai->slot_width); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_parse_clk_codec(dev, codec_ep, dai_link, codec_dai); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_canonicalize_dailink(dai_link); + if (ret < 0) + goto dai_link_of_err; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "%s-%s", + dai_link->cpu_dai_name, + dai_link->codec_dai_name); + if (ret < 0) + goto dai_link_of_err; + + dai_link->ops = &asoc_graph_card_ops; + dai_link->init = asoc_graph_card_dai_init; + + dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); + dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); + dev_dbg(dev, "\tcpu : %s / %d\n", + dai_link->cpu_dai_name, + dai_props->cpu_dai.sysclk); + dev_dbg(dev, "\tcodec : %s / %d\n", + dai_link->codec_dai_name, + dai_props->codec_dai.sysclk); + + asoc_simple_card_canonicalize_cpu(dai_link, + priv->snd_card.num_links == 1); + +dai_link_of_err: + of_node_put(codec_ep); + + return ret; +} + +static int asoc_graph_card_parse_of(struct device *dev, + struct graph_card_data *priv) +{ + struct device_node *node = dev->of_node; + struct snd_soc_card *card = &priv->snd_card; + struct device_node *np; + int idx = 0; + int ret; + + /* + * we need to consider "widgets", "routing", "mclk-fs" around here + * see simple-card + */ + + while ((np = of_parse_phandle(node, "dais", idx))) { + ret = asoc_graph_card_dai_link_of(np, priv, idx++); + if (ret < 0) + return ret; + } + + return asoc_simple_card_parse_card_name(card, NULL); +} + +static int asoc_graph_get_dais_count(struct device *dev) +{ + struct device_node *node = dev->of_node; + int count = 0; + + if (of_get_property(node, "dais", NULL)) { + while (of_parse_phandle(node, "dais", count)) + count++; + } else { + count = 1; + } + + return count; +} + +static int asoc_graph_card_probe(struct platform_device *pdev) +{ + struct graph_card_data *priv; + struct snd_soc_dai_link *dai_link; + struct graph_dai_props *dai_props; + struct device *dev = &pdev->dev; + int num, ret; + + /* Allocate the private data and the DAI link array */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + num = asoc_graph_get_dais_count(dev); + + dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); + dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); + if (!dai_props || !dai_link) + return -ENOMEM; + + priv->dai_props = dai_props; + priv->dai_link = dai_link; + + /* Init snd_soc_card */ + priv->snd_card.owner = THIS_MODULE; + priv->snd_card.dev = dev; + priv->snd_card.dai_link = dai_link; + priv->snd_card.num_links = num; + + ret = asoc_graph_card_parse_of(dev, priv); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "parse error %d\n", ret); + goto err; + } + + snd_soc_card_set_drvdata(&priv->snd_card, priv); + + ret = devm_snd_soc_register_card(dev, &priv->snd_card); + if (ret >= 0) + return ret; +err: + asoc_simple_card_clean_reference(&priv->snd_card); + + return ret; +} + +static int asoc_graph_card_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct graph_card_data *priv = snd_soc_card_get_drvdata(card); + + return asoc_simple_card_clean_reference(&priv->snd_card); +} + +static const struct of_device_id asoc_graph_of_match[] = { + { .compatible = "audio-graph-card", }, + {}, +}; +MODULE_DEVICE_TABLE(of, asoc_graph_of_match); + +static struct platform_driver asoc_graph_card = { + .driver = { + .name = "asoc-audio-graph-card", + .of_match_table = asoc_graph_of_match, + }, + .probe = asoc_graph_card_probe, + .remove = asoc_graph_card_remove, +}; +module_platform_driver(asoc_graph_card); + +MODULE_ALIAS("platform:asoc-audio-graph-card"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC Audio Graph Sound Card"); +MODULE_AUTHOR("Kuninori Morimoto kuninori.morimoto.gx@renesas.com");
On Mon, Mar 13, 2017 at 12:54 AM, Kuninori Morimoto kuninori.morimoto.gx@renesas.com wrote:
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
OF-graph base DT binding are used on V4L2, and ALSA SoC is using different style of DT today. Now ALSA SoC supports simple-card driver for generic/simple sound card. In the future, V4L2 / ALSA will support HDMI, and then, DT bindings between V4L2 / ALSA should be merged. This patch adds new Audio Graph Card which is OF-graph base of simple-card
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
v3 -> v4
- use "label"
- use "dai-format"
- don't use prefix
sound/soc/generic/Kconfig | 8 + sound/soc/generic/Makefile | 2 + sound/soc/generic/audio-graph-card.c | 301 +++++++++++++++++++++++++++++++++++ 3 files changed, 311 insertions(+) create mode 100644 sound/soc/generic/audio-graph-card.c
diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index d023959..121a48e 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -14,3 +14,11 @@ config SND_SIMPLE_SCU_CARD help This option enables generic simple SCU sound card support. It supports DPCM of multi CPU single Codec system.
+config SND_AUDIO_GRAPH_CARD
tristate "ASoC Audio Graph sound card support"
depends on OF
select SND_SIMPLE_CARD_UTILS
help
This option enables generic simple simple sound card support
with OF-graph DT bindings.
diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index ee750f3..670068f 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile @@ -1,7 +1,9 @@ snd-soc-simple-card-utils-objs := simple-card-utils.o snd-soc-simple-card-objs := simple-card.o snd-soc-simple-scu-card-objs := simple-scu-card.o +snd-soc-audio-graph-card-objs := audio-graph-card.o
obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o +obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c new file mode 100644 index 0000000..07afdfa --- /dev/null +++ b/sound/soc/generic/audio-graph-card.c @@ -0,0 +1,301 @@ +/*
- ASoC audio graph sound card support
- Copyright (C) 2016 Renesas Solutions Corp.
- Kuninori Morimoto kuninori.morimoto.gx@renesas.com
- based on ${LINUX}/sound/soc/generic/simple-card.c
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- */
+#include <linux/clk.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/string.h> +#include <sound/jack.h> +#include <sound/simple_card_utils.h>
+struct graph_card_data {
struct snd_soc_card snd_card;
struct graph_dai_props {
struct asoc_simple_dai cpu_dai;
struct asoc_simple_dai codec_dai;
} *dai_props;
struct snd_soc_dai_link *dai_link;
+};
+#define graph_priv_to_dev(priv) ((priv)->snd_card.dev) +#define graph_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i)) +#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i))
+static int asoc_graph_card_startup(struct snd_pcm_substream *substream) +{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
struct graph_dai_props *dai_props =
graph_priv_to_props(priv, rtd->num);
int ret;
ret = clk_prepare_enable(dai_props->cpu_dai.clk);
if (ret)
return ret;
ret = clk_prepare_enable(dai_props->codec_dai.clk);
if (ret)
clk_disable_unprepare(dai_props->cpu_dai.clk);
return ret;
+}
+static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) +{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
struct graph_dai_props *dai_props =
graph_priv_to_props(priv, rtd->num);
clk_disable_unprepare(dai_props->cpu_dai.clk);
clk_disable_unprepare(dai_props->codec_dai.clk);
+}
+static struct snd_soc_ops asoc_graph_card_ops = {
.startup = asoc_graph_card_startup,
.shutdown = asoc_graph_card_shutdown,
+};
+static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) +{
struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *codec = rtd->codec_dai;
struct snd_soc_dai *cpu = rtd->cpu_dai;
struct graph_dai_props *dai_props =
graph_priv_to_props(priv, rtd->num);
int ret;
ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai);
if (ret < 0)
return ret;
ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai);
if (ret < 0)
return ret;
return 0;
+}
+static int asoc_graph_card_dai_link_of(struct device_node *cpu_port,
struct graph_card_data *priv,
int idx)
+{
struct device *dev = graph_priv_to_dev(priv);
struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx);
struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx);
struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
struct device_node *cpu_ep = of_get_next_child(cpu_port, NULL);
struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep);
struct device_node *rcpu_ep = of_graph_get_remote_endpoint(codec_ep);
int ret;
if (rcpu_ep != cpu_ep) {
dev_err(dev, "remote-endpoint missmatch (%s/%s/%s)\n",
cpu_ep->name, codec_ep->name, rcpu_ep->name);
return -EINVAL;
}
We should have this check in dtc rather than the kernel.
ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep,
NULL, &dai_link->dai_fmt);
if (ret < 0)
goto dai_link_of_err;
/*
* we need to consider "mclk-fs" around here
* see simple-card
*/
ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link);
if (ret < 0)
goto dai_link_of_err;
ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link);
if (ret < 0)
goto dai_link_of_err;
ret = snd_soc_of_parse_tdm_slot(cpu_ep,
&cpu_dai->tx_slot_mask,
&cpu_dai->rx_slot_mask,
&cpu_dai->slots,
&cpu_dai->slot_width);
if (ret < 0)
goto dai_link_of_err;
ret = snd_soc_of_parse_tdm_slot(codec_ep,
&codec_dai->tx_slot_mask,
&codec_dai->rx_slot_mask,
&codec_dai->slots,
&codec_dai->slot_width);
if (ret < 0)
goto dai_link_of_err;
ret = asoc_simple_card_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai);
if (ret < 0)
goto dai_link_of_err;
ret = asoc_simple_card_parse_clk_codec(dev, codec_ep, dai_link, codec_dai);
if (ret < 0)
goto dai_link_of_err;
ret = asoc_simple_card_canonicalize_dailink(dai_link);
if (ret < 0)
goto dai_link_of_err;
ret = asoc_simple_card_set_dailink_name(dev, dai_link,
"%s-%s",
dai_link->cpu_dai_name,
dai_link->codec_dai_name);
if (ret < 0)
goto dai_link_of_err;
dai_link->ops = &asoc_graph_card_ops;
dai_link->init = asoc_graph_card_dai_init;
dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt);
dev_dbg(dev, "\tcpu : %s / %d\n",
dai_link->cpu_dai_name,
dai_props->cpu_dai.sysclk);
dev_dbg(dev, "\tcodec : %s / %d\n",
dai_link->codec_dai_name,
dai_props->codec_dai.sysclk);
asoc_simple_card_canonicalize_cpu(dai_link,
priv->snd_card.num_links == 1);
+dai_link_of_err:
of_node_put(codec_ep);
return ret;
+}
+static int asoc_graph_card_parse_of(struct device *dev,
struct graph_card_data *priv)
+{
struct device_node *node = dev->of_node;
struct snd_soc_card *card = &priv->snd_card;
struct device_node *np;
int idx = 0;
int ret;
/*
* we need to consider "widgets", "routing", "mclk-fs" around here
* see simple-card
*/
while ((np = of_parse_phandle(node, "dais", idx))) {
of_for_each_phandle
ret = asoc_graph_card_dai_link_of(np, priv, idx++);
if (ret < 0)
return ret;
}
return asoc_simple_card_parse_card_name(card, NULL);
+}
+static int asoc_graph_get_dais_count(struct device *dev) +{
struct device_node *node = dev->of_node;
int count = 0;
if (of_get_property(node, "dais", NULL)) {
while (of_parse_phandle(node, "dais", count))
of_for_each_phandle
count++;
} else {
count = 1;
This should be an error or 0, right?
}
return count;
+}
+static int asoc_graph_card_probe(struct platform_device *pdev) +{
struct graph_card_data *priv;
struct snd_soc_dai_link *dai_link;
struct graph_dai_props *dai_props;
struct device *dev = &pdev->dev;
int num, ret;
/* Allocate the private data and the DAI link array */
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
num = asoc_graph_get_dais_count(dev);
dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL);
dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL);
if (!dai_props || !dai_link)
return -ENOMEM;
priv->dai_props = dai_props;
priv->dai_link = dai_link;
/* Init snd_soc_card */
priv->snd_card.owner = THIS_MODULE;
priv->snd_card.dev = dev;
priv->snd_card.dai_link = dai_link;
priv->snd_card.num_links = num;
ret = asoc_graph_card_parse_of(dev, priv);
if (ret < 0) {
if (ret != -EPROBE_DEFER)
dev_err(dev, "parse error %d\n", ret);
goto err;
}
snd_soc_card_set_drvdata(&priv->snd_card, priv);
ret = devm_snd_soc_register_card(dev, &priv->snd_card);
if (ret >= 0)
return ret;
+err:
asoc_simple_card_clean_reference(&priv->snd_card);
return ret;
+}
+static int asoc_graph_card_remove(struct platform_device *pdev) +{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct graph_card_data *priv = snd_soc_card_get_drvdata(card);
return asoc_simple_card_clean_reference(&priv->snd_card);
+}
+static const struct of_device_id asoc_graph_of_match[] = {
{ .compatible = "audio-graph-card", },
{},
+}; +MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
+static struct platform_driver asoc_graph_card = {
.driver = {
.name = "asoc-audio-graph-card",
.of_match_table = asoc_graph_of_match,
},
.probe = asoc_graph_card_probe,
.remove = asoc_graph_card_remove,
+}; +module_platform_driver(asoc_graph_card);
+MODULE_ALIAS("platform:asoc-audio-graph-card"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC Audio Graph Sound Card");
+MODULE_AUTHOR("Kuninori Morimoto kuninori.morimoto.gx@renesas.com");
1.9.1
Hi Rob
Thank you for your review
if (rcpu_ep != cpu_ep) {
dev_err(dev, "remote-endpoint missmatch (%s/%s/%s)\n",
cpu_ep->name, codec_ep->name, rcpu_ep->name);
return -EINVAL;
}
We should have this check in dtc rather than the kernel.
We can remove this if dtc could support it
while ((np = of_parse_phandle(node, "dais", idx))) {
of_for_each_phandle
(snip)
if (of_get_property(node, "dais", NULL)) {
while (of_parse_phandle(node, "dais", count))
of_for_each_phandle
Thanks. will use it
count++;
} else {
count = 1;
This should be an error or 0, right?
Yes, thanks. will do
Best regards --- Kuninori Morimoto
participants (4)
-
Kuninori Morimoto
-
Mark Brown
-
Rob Herring
-
Rob Herring