[alsa-devel] [resend][PATCH v5 00/10] ASoC: add OF-graph base simple-card
Hi Rob, Mark
+2 weeks passed. I will re-post these patches.
1) - 4): OF-graph base patches (= Rot) 5) - 10): ALSA SoC patches (= Mark)
Kuninori Morimoto (10): 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: simple-card-utils: enable "label" on asoc_simple_card_parse_card_name 6) ASoC: soc-core: enable "dai-name" on snd_soc_of_parse_daifmt() 7) ASoC: add snd_soc_get_dai_id() 8) ASoC: simple-card-utils: add asoc_simple_card_parse_graph_dai() 9) ASoC: add audio-graph-card document 10) ASoC: add audio-graph-card support
.../devicetree/bindings/sound/audio-graph-card.txt | 124 +++++++++ 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 | 308 +++++++++++++++++++++ sound/soc/generic/simple-card-utils.c | 54 +++- sound/soc/soc-core.c | 32 ++- 10 files changed, 604 insertions(+), 16 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
of_for_each_phandle() uses of_phandle_iterator_init/next but these aren'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 solves this issue
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- This patch is related to this patch-set Subject: [PATCH v5 0/9] ASoC: add OF-graph base simple-card Date: Tue, 21 Mar 2017 14:17:03 +0900
drivers/of/base.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/drivers/of/base.c b/drivers/of/base.c index 812edb9..bc42f91 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,
On Sun, Apr 16, 2017 at 7:21 PM, Kuninori Morimoto kuninori.morimoto.gx@renesas.com wrote:
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
of_for_each_phandle() uses of_phandle_iterator_init/next but these aren'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 solves this issue
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Acked-by: Rob Herring robh@kernel.org
Hi Rob
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
of_for_each_phandle() uses of_phandle_iterator_init/next but these aren'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 solves this issue
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Acked-by: Rob Herring robh@kernel.org
Thanks. But I wonder you are the maintainer of of_graph ?
Best regards --- Kuninori Morimoto
On Mon, Apr 17, 2017 at 7:05 PM, Kuninori Morimoto kuninori.morimoto.gx@renesas.com wrote:
Hi Rob
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
of_for_each_phandle() uses of_phandle_iterator_init/next but these aren'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 solves this issue
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Acked-by: Rob Herring robh@kernel.org
Thanks. But I wonder you are the maintainer of of_graph ?
get_maintainers.pl can answer that for you: yes.
Rob
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 --- v4 -> v5
- 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 --- v4 -> v5
- 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 --- v4 -> v5
- 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) {
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 --- v4 -> v5
- don't exchange simle-xxx-card side
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 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 Sun, Apr 16, 2017 at 7:22 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
v4 -> v5
- don't exchange simle-xxx-card side
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 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]);
"<prefix>label" is not a valid property name.
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;
-- 1.9.1
Hi Rob
@@ -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]);
"<prefix>label" is not a valid property name.
This function will be used from many drivers. Not only for OF-graph sound card. OF-graph sound card will use it without prefix.
Best regards --- Kuninori Morimoto
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 enables [prefix]dai-format, too.
Signed-off-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- v4 -> v5
- don't exchange simle-xxx-card side
sound/soc/soc-core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d267a01..f8608b7 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3946,11 +3946,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 Sun, Apr 16, 2017 at 7:23 PM, Kuninori Morimoto kuninori.morimoto.gx@renesas.com wrote:
From: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Subject should be dai-format, not dai-name.
Current snd_soc_of_parse_daifmt() detects [prefix]format, but "format" was unclear in some case. This patch enables [prefix]dai-format, too.
prefix is not valid here either.
Hi Rob
Subject should be dai-format, not dai-name.
Oops ! Will fix in v6
Current snd_soc_of_parse_daifmt() detects [prefix]format, but "format" was unclear in some case. This patch enables [prefix]dai-format, too.
prefix is not valid here either.
This function will be used from many drivers. OF-graph sound card will use it without prefix.
Best regards --- Kuninori Morimoto
On Mon, Apr 17, 2017 at 7:00 PM, Kuninori Morimoto kuninori.morimoto.gx@renesas.com wrote:
Hi Rob
Subject should be dai-format, not dai-name.
Oops ! Will fix in v6
Current snd_soc_of_parse_daifmt() detects [prefix]format, but "format" was unclear in some case. This patch enables [prefix]dai-format, too.
prefix is not valid here either.
This function will be used from many drivers. OF-graph sound card will use it without prefix.
Only the legacy [prefix]format or new dai-format are valid. [prefix]dai-format should not be allowed.
Rob
Hi Rob
This function will be used from many drivers. OF-graph sound card will use it without prefix.
Only the legacy [prefix]format or new dai-format are valid. [prefix]dai-format should not be allowed.
Ahh, OK. Will fix
Best regards --- Kuninori Morimoto
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 --- v4 -> v5
- 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 ee838b0..af73160 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1663,6 +1663,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 f8608b7..0c75b0d 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> @@ -4035,6 +4036,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 Sun, Apr 16, 2017 at 7:23 PM, 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
v4 -> v5
- no change
You can keep posting the same version again, but I still have the same objections.
Having the core graph-card parsing details of remote endpoints is fragile. The driver for the remote node should do the parsing of its own endpoints and is the only entity that should care or know what the port and endpoint numbering is. The fact that you are planning to change this for HDMI support is evidence of that. I don't see the point of a temporary solution.
Rob
Hi Rob
Having the core graph-card parsing details of remote endpoints is fragile. The driver for the remote node should do the parsing of its own endpoints and is the only entity that should care or know what the port and endpoint numbering is. The fact that you are planning to change this for HDMI support is evidence of that. I don't see the point of a temporary solution.
Indeed. OK, I will move it from soc-core to audio-graph-card in v6 Thanks
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 --- v4 -> v5
- 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) {
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 --- v4 -> v5
- added Rob's Reviewed-by
.../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"; + ... + }; + }; + }; + }; +
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 --- v4 -> v5
- added graph_priv_to_card() - use of_for_each_phandle()
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 (2)
-
Kuninori Morimoto
-
Rob Herring