[alsa-devel] [PATCH v6 0/9] ASoC: add OF-graph base simple-card
![](https://secure.gravatar.com/avatar/2ea1ea42072986bc3dd023464d0c2de6.jpg?s=120&d=mm&r=g)
Hi Mark, Rob
These are v6 of OF-graph sound card support. Main change is asoc_simple_card_get_dai_id() becames simple-card-utils's static function.
1) - 4) : OF-graph base functions 5) - 9) : OF-graph + simple-card (= audio_graph_card)
Kuninori Morimoto (9): 1) of-graph: export symbol of_phandle_iterator_init/next 2) of_graph: add of_graph_get_remote_endpoint() 3) of_graph: add of_graph_get_port_parent() 4) of_graph: add of_graph_get_endpoint_count() 5) ASoC: soc-core: enable "dai-format" on snd_soc_of_parse_daifmt() 6) ASoC: simple-card-utils: enable "label" on asoc_simple_card_parse_card_name 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 +++++++++ drivers/of/base.c | 62 ++++- include/linux/of_graph.h | 21 ++ include/sound/simple_card_utils.h | 10 + sound/soc/generic/Kconfig | 8 + sound/soc/generic/Makefile | 2 + sound/soc/generic/audio-graph-card.c | 308 +++++++++++++++++++++ sound/soc/generic/simple-card-utils.c | 75 ++++- sound/soc/soc-core.c | 10 +- 9 files changed, 603 insertions(+), 17 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/audio-graph-card.txt create mode 100644 sound/soc/generic/audio-graph-card.c
![](https://secure.gravatar.com/avatar/2ea1ea42072986bc3dd023464d0c2de6.jpg?s=120&d=mm&r=g)
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
of_for_each_phandle() uses of_phandle_iterator_init/next but it isn't exported. So kernel module complile will say
ERROR: "of_phandle_iterator_init" [xxx.ko] undefined! ERROR: "of_phandle_iterator_next" [xxx.ko] undefined!
This patch solved this issue
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com Acked-by: Rob Herring robh@kernel.org --- v5 -> v6
- add Rob's Acked-by
drivers/of/base.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/drivers/of/base.c b/drivers/of/base.c index d7c4629..d9adaa9 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1570,6 +1570,7 @@ int of_phandle_iterator_init(struct of_phandle_iterator *it,
return 0; } +EXPORT_SYMBOL_GPL(of_phandle_iterator_init);
int of_phandle_iterator_next(struct of_phandle_iterator *it) { @@ -1639,6 +1640,7 @@ int of_phandle_iterator_next(struct of_phandle_iterator *it)
return -EINVAL; } +EXPORT_SYMBOL_GPL(of_phandle_iterator_next);
int of_phandle_iterator_args(struct of_phandle_iterator *it, uint32_t *args,
![](https://secure.gravatar.com/avatar/2ea1ea42072986bc3dd023464d0c2de6.jpg?s=120&d=mm&r=g)
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 --- v5 -> v6
- 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 d9adaa9..1ae2510 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2457,6 +2457,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 * @@ -2470,7 +2484,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--) { @@ -2494,7 +2508,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) {
![](https://secure.gravatar.com/avatar/2ea1ea42072986bc3dd023464d0c2de6.jpg?s=120&d=mm&r=g)
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 --- v5 -> v6
- 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 1ae2510..acda15b 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2471,6 +2471,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 * @@ -2481,18 +2502,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) {
![](https://secure.gravatar.com/avatar/2ea1ea42072986bc3dd023464d0c2de6.jpg?s=120&d=mm&r=g)
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 --- v5 -> v6
- 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 acda15b..bc42f91 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2529,6 +2529,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) {
![](https://secure.gravatar.com/avatar/2ea1ea42072986bc3dd023464d0c2de6.jpg?s=120&d=mm&r=g)
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Current snd_soc_of_parse_daifmt() detects [prefix]format, but "format" was unclear in some case. This patch checks "dai-format" first, and try to check "[prefix]format" if "dai-format" was not exist.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v5 -> v6
- "dai-format" on Subject - don't allow [prefix] on dai-format
sound/soc/soc-core.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 9ee4d4a..4a20c30 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3950,11 +3950,15 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, prefix = "";
/* - * check "[prefix]format = xxx" + * check "dai-format = xxx" + * or "[prefix]format = xxx" * SND_SOC_DAIFMT_FORMAT_MASK area */ - snprintf(prop, sizeof(prop), "%sformat", prefix); - ret = of_property_read_string(np, prop, &str); + ret = of_property_read_string(np, "dai-format", &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) {
![](https://secure.gravatar.com/avatar/d930951cb00393ecf9c3db3a56d78fa9.jpg?s=120&d=mm&r=g)
The patch
ASoC: soc-core: enable "dai-format" on snd_soc_of_parse_daifmt()
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 5711c97920377827da4bbc18233ffb08c6e3bbef Mon Sep 17 00:00:00 2001
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com Date: Thu, 20 Apr 2017 01:33:24 +0000 Subject: [PATCH] ASoC: soc-core: enable "dai-format" on snd_soc_of_parse_daifmt()
Current snd_soc_of_parse_daifmt() detects [prefix]format, but "format" was unclear in some case. This patch checks "dai-format" first, and try to check "[prefix]format" if "dai-format" was not exist.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/soc-core.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index aae099c0e502..ab7a07f86ab8 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3960,11 +3960,15 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np, prefix = "";
/* - * check "[prefix]format = xxx" + * check "dai-format = xxx" + * or "[prefix]format = xxx" * SND_SOC_DAIFMT_FORMAT_MASK area */ - snprintf(prop, sizeof(prop), "%sformat", prefix); - ret = of_property_read_string(np, prop, &str); + ret = of_property_read_string(np, "dai-format", &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) {
![](https://secure.gravatar.com/avatar/2ea1ea42072986bc3dd023464d0c2de6.jpg?s=120&d=mm&r=g)
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 enables [prefix]label too.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v5 -> v6
- used const for names[]
sound/soc/generic/simple-card-utils.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 343b291..94ceaca 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -81,15 +81,25 @@ int asoc_simple_card_set_dailink_name(struct device *dev, int asoc_simple_card_parse_card_name(struct snd_soc_card *card, char *prefix) { + char * const names[] = { + "label", "name" + }; char prop[128]; + 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;
![](https://secure.gravatar.com/avatar/f11e8548de3dfbbcd3a015ff24ce630d.jpg?s=120&d=mm&r=g)
On Mon, Apr 17, 2017 at 9:44 PM, Kuninori Morimoto kuninori.morimoto.gx@renesas.com 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 enables [prefix]label too.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
v5 -> v6
- used const for names[]
sound/soc/generic/simple-card-utils.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 343b291..94ceaca 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -81,15 +81,25 @@ int asoc_simple_card_set_dailink_name(struct device *dev, int asoc_simple_card_parse_card_name(struct snd_soc_card *card, char *prefix) {
char * const names[] = {
"label", "name"
}; char prop[128];
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;
}
This is still wrong as you are allowing "<prefix>label" for property names. I think you want something like this:
ret = snd_soc_of_parse_card_name(card, "label"); if (ret < 0) { char prop[128]; snprintf(prop, sizeof(prop), "%sname", prefix); /* Parse the card name from DT */ ret = snd_soc_of_parse_card_name(card, prop); if (ret < 0) return ret; }
![](https://secure.gravatar.com/avatar/2ea1ea42072986bc3dd023464d0c2de6.jpg?s=120&d=mm&r=g)
Hi Rob
int asoc_simple_card_parse_card_name(struct snd_soc_card *card, char *prefix) {
char * const names[] = {
"label", "name"
}; char prop[128];
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;
}
This is still wrong as you are allowing "<prefix>label" for property names. I think you want something like this:
ret = snd_soc_of_parse_card_name(card, "label"); if (ret < 0) { char prop[128]; snprintf(prop, sizeof(prop), "%sname", prefix); /* Parse the card name from DT */ ret = snd_soc_of_parse_card_name(card, prop); if (ret < 0) return ret; }
OK, will fix
Best regards --- Kuninori Morimoto
![](https://secure.gravatar.com/avatar/2ea1ea42072986bc3dd023464d0c2de6.jpg?s=120&d=mm&r=g)
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 --- v5 -> v6
- asoc_simple_card_get_dai_id() is merged into this function
include/sound/simple_card_utils.h | 10 ++++++ sound/soc/generic/simple-card-utils.c | 57 +++++++++++++++++++++++++++++++++++ 2 files changed, 67 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 94ceaca..7575b6b 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, @@ -175,6 +176,62 @@ int asoc_simple_card_parse_dai(struct device_node *node, } EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai);
+static int asoc_simple_card_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; +} + +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] = asoc_simple_card_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) {
![](https://secure.gravatar.com/avatar/d930951cb00393ecf9c3db3a56d78fa9.jpg?s=120&d=mm&r=g)
The patch
ASoC: simple-card-utils: add asoc_simple_card_parse_graph_dai()
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 1689333f8311f5952ee69d64adf242028dc7e6c6 Mon Sep 17 00:00:00 2001
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com Date: Thu, 20 Apr 2017 01:35:18 +0000 Subject: [PATCH] ASoC: simple-card-utils: add asoc_simple_card_parse_graph_dai()
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 Signed-off-by: Mark Brown broonie@kernel.org --- include/sound/simple_card_utils.h | 10 ++++++ sound/soc/generic/simple-card-utils.c | 57 +++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+)
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index af58d2362975..efab584af11b 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 c5ab8ad2a5ce..5a3d51e45938 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, @@ -171,6 +172,62 @@ int asoc_simple_card_parse_dai(struct device_node *node, } EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai);
+static int asoc_simple_card_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; +} + +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] = asoc_simple_card_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) {
![](https://secure.gravatar.com/avatar/2ea1ea42072986bc3dd023464d0c2de6.jpg?s=120&d=mm&r=g)
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 Reviewed-by: Rob Herring robh@kernel.org --- v5 -> v6
- no change
.../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"; + ... + }; + }; + }; + }; +
![](https://secure.gravatar.com/avatar/2ea1ea42072986bc3dd023464d0c2de6.jpg?s=120&d=mm&r=g)
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 --- v5 -> v6
- no change
sound/soc/generic/Kconfig | 8 + sound/soc/generic/Makefile | 2 + sound/soc/generic/audio-graph-card.c | 308 +++++++++++++++++++++++++++++++++++ 3 files changed, 318 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..07e010d --- /dev/null +++ b/sound/soc/generic/audio-graph-card.c @@ -0,0 +1,308 @@ +/* + * 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_card(priv) (&(priv)->snd_card) +#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i)) +#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) +#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (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 snd_soc_card *card = graph_priv_to_card(priv); + 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); + ret = -EINVAL; + goto dai_link_of_err; + } + + 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, + cpu_dai->sysclk); + dev_dbg(dev, "\tcodec : %s / %d\n", + dai_link->codec_dai_name, + codec_dai->sysclk); + + asoc_simple_card_canonicalize_cpu(dai_link, + card->num_links == 1); + +dai_link_of_err: + of_node_put(cpu_ep); + of_node_put(rcpu_ep); + of_node_put(codec_ep); + + return ret; +} + +static int asoc_graph_card_parse_of(struct graph_card_data *priv) +{ + struct of_phandle_iterator it; + struct device *dev = graph_priv_to_dev(priv); + struct snd_soc_card *card = graph_priv_to_card(priv); + struct device_node *node = dev->of_node; + int rc, idx = 0; + int ret; + + /* + * we need to consider "widgets", "routing", "mclk-fs" around here + * see simple-card + */ + + of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { + ret = asoc_graph_card_dai_link_of(it.node, priv, idx++); + of_node_put(it.node); + 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 of_phandle_iterator it; + struct device_node *node = dev->of_node; + int count = 0; + int rc; + + of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { + count++; + of_node_put(it.node); + } + + 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; + struct snd_soc_card *card; + 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); + if (num == 0) + return -EINVAL; + + 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 */ + card = graph_priv_to_card(priv); + card->owner = THIS_MODULE; + card->dev = dev; + card->dai_link = dai_link; + card->num_links = num; + + ret = asoc_graph_card_parse_of(priv); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "parse error %d\n", ret); + goto err; + } + + snd_soc_card_set_drvdata(card, priv); + + ret = devm_snd_soc_register_card(dev, card); + if (ret >= 0) + return ret; +err: + asoc_simple_card_clean_reference(card); + + return ret; +} + +static int asoc_graph_card_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + return asoc_simple_card_clean_reference(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");
participants (3)
-
Kuninori Morimoto
-
Mark Brown
-
Rob Herring