aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/sound/audio-graph-card.txt124
-rw-r--r--Documentation/devicetree/bindings/sound/simple-scu-card.txt65
-rw-r--r--drivers/of/base.c62
-rw-r--r--include/linux/of_graph.h21
-rw-r--r--include/sound/simple_card_utils.h10
-rw-r--r--include/sound/soc.h3
-rw-r--r--sound/soc/generic/Kconfig8
-rw-r--r--sound/soc/generic/Makefile2
-rw-r--r--sound/soc/generic/audio-graph-card.c310
-rw-r--r--sound/soc/generic/simple-card-utils.c82
-rw-r--r--sound/soc/soc-core.c47
11 files changed, 675 insertions, 59 deletions
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 @@
1Audio Graph Card:
2
3Audio Graph Card specifies audio DAI connections of SoC <-> codec.
4It is based on common bindings for device graphs.
5see ${LINUX}/Documentation/devicetree/bindings/graph.txt
6
7Basically, Audio Graph Card property is same as Simple Card.
8see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt
9
10Below are same as Simple-Card.
11
12- label
13- dai-format
14- frame-master
15- bitclock-master
16- bitclock-inversion
17- frame-inversion
18- dai-tdm-slot-num
19- dai-tdm-slot-width
20- clocks / system-clock-frequency
21
22Required properties:
23
24- compatible : "audio-graph-card";
25- dais : list of CPU DAI port{s}
26
27Example: Single DAI case
28
29 sound_card {
30 compatible = "audio-graph-card";
31
32 dais = <&cpu_port>;
33 };
34
35 dai-controller {
36 ...
37 cpu_port: port {
38 cpu_endpoint: endpoint {
39 remote-endpoint = <&codec_endpoint>;
40
41 dai-format = "left_j";
42 ...
43 };
44 };
45 };
46
47 audio-codec {
48 ...
49 port {
50 codec_endpoint: endpoint {
51 remote-endpoint = <&cpu_endpoint>;
52 };
53 };
54 };
55
56Example: Multi DAI case
57
58 sound-card {
59 compatible = "audio-graph-card";
60
61 label = "sound-card";
62
63 dais = <&cpu_port0
64 &cpu_port1
65 &cpu_port2>;
66 };
67
68 audio-codec@0 {
69 ...
70 port {
71 codec0_endpoint: endpoint {
72 remote-endpoint = <&cpu_endpoint0>;
73 };
74 };
75 };
76
77 audio-codec@1 {
78 ...
79 port {
80 codec1_endpoint: endpoint {
81 remote-endpoint = <&cpu_endpoint1>;
82 };
83 };
84 };
85
86 audio-codec@2 {
87 ...
88 port {
89 codec2_endpoint: endpoint {
90 remote-endpoint = <&cpu_endpoint2>;
91 };
92 };
93 };
94
95 dai-controller {
96 ...
97 ports {
98 cpu_port0: port@0 {
99 cpu_endpoint0: endpoint {
100 remote-endpoint = <&codec0_endpoint>;
101
102 dai-format = "left_j";
103 ...
104 };
105 };
106 cpu_port1: port@1 {
107 cpu_endpoint1: endpoint {
108 remote-endpoint = <&codec1_endpoint>;
109
110 dai-format = "i2s";
111 ...
112 };
113 };
114 cpu_port2: port@2 {
115 cpu_endpoint2: endpoint {
116 remote-endpoint = <&codec2_endpoint>;
117
118 dai-format = "i2s";
119 ...
120 };
121 };
122 };
123 };
124
diff --git a/Documentation/devicetree/bindings/sound/simple-scu-card.txt b/Documentation/devicetree/bindings/sound/simple-scu-card.txt
index d6fe47ed09af..e894cef1d314 100644
--- a/Documentation/devicetree/bindings/sound/simple-scu-card.txt
+++ b/Documentation/devicetree/bindings/sound/simple-scu-card.txt
@@ -1,35 +1,29 @@
1ASoC simple SCU Sound Card 1ASoC Simple SCU Sound Card
2 2
3Simple-Card specifies audio DAI connections of SoC <-> codec. 3Simple SCU Sound Card is "Simple Sound Card" + "ALSA DPCM".
4For example, you can use this driver if you want to exchange sampling rate convert,
5Mixing, etc...
4 6
5Required properties: 7Required properties:
6 8
7- compatible : "simple-scu-audio-card" 9- compatible : "simple-scu-audio-card"
8 "renesas,rsrc-card" 10 "renesas,rsrc-card"
9
10Optional properties: 11Optional properties:
11 12
12- simple-audio-card,name : User specified audio sound card name, one string 13- simple-audio-card,name : see simple-audio-card.txt
13 property. 14- simple-audio-card,cpu : see simple-audio-card.txt
14- simple-audio-card,cpu : CPU sub-node 15- simple-audio-card,codec : see simple-audio-card.txt
15- simple-audio-card,codec : CODEC sub-node
16 16
17Optional subnode properties: 17Optional subnode properties:
18 18
19- simple-audio-card,format : CPU/CODEC common audio format. 19- simple-audio-card,format : see simple-audio-card.txt
20 "i2s", "right_j", "left_j" , "dsp_a" 20- simple-audio-card,frame-master : see simple-audio-card.txt
21 "dsp_b", "ac97", "pdm", "msb", "lsb" 21- simple-audio-card,bitclock-master : see simple-audio-card.txt
22- simple-audio-card,frame-master : Indicates dai-link frame master. 22- simple-audio-card,bitclock-inversion : see simple-audio-card.txt
23 phandle to a cpu or codec subnode. 23- simple-audio-card,frame-inversion : see simple-audio-card.txt
24- simple-audio-card,bitclock-master : Indicates dai-link bit clock master.
25 phandle to a cpu or codec subnode.
26- simple-audio-card,bitclock-inversion : bool property. Add this if the
27 dai-link uses bit clock inversion.
28- simple-audio-card,frame-inversion : bool property. Add this if the
29 dai-link uses frame clock inversion.
30- simple-audio-card,convert-rate : platform specified sampling rate convert 24- simple-audio-card,convert-rate : platform specified sampling rate convert
31- simple-audio-card,convert-channels : platform specified converted channel size (2 - 8 ch) 25- simple-audio-card,convert-channels : platform specified converted channel size (2 - 8 ch)
32- simple-audio-card,prefix : see audio-routing 26- simple-audio-card,prefix : see routing
33- simple-audio-card,routing : A list of the connections between audio components. 27- simple-audio-card,routing : A list of the connections between audio components.
34 Each entry is a pair of strings, the first being the connection's sink, 28 Each entry is a pair of strings, the first being the connection's sink,
35 the second being the connection's source. Valid names for sources. 29 the second being the connection's source. Valid names for sources.
@@ -38,19 +32,11 @@ Optional subnode properties:
38 32
39Required CPU/CODEC subnodes properties: 33Required CPU/CODEC subnodes properties:
40 34
41- sound-dai : phandle and port of CPU/CODEC 35- sound-dai : see simple-audio-card.txt
42 36
43Optional CPU/CODEC subnodes properties: 37Optional CPU/CODEC subnodes properties:
44 38
45- clocks / system-clock-frequency : specify subnode's clock if needed. 39- clocks / system-clock-frequency : see simple-audio-card.txt
46 it can be specified via "clocks" if system has
47 clock node (= common clock), or "system-clock-frequency"
48 (if system doens't support common clock)
49 If a clock is specified, it is
50 enabled with clk_prepare_enable()
51 in dai startup() and disabled with
52 clk_disable_unprepare() in dai
53 shutdown().
54 40
55Example 1. Sampling Rate Covert 41Example 1. Sampling Rate Covert
56 42
@@ -59,11 +45,10 @@ sound {
59 45
60 simple-audio-card,name = "rsnd-ak4643"; 46 simple-audio-card,name = "rsnd-ak4643";
61 simple-audio-card,format = "left_j"; 47 simple-audio-card,format = "left_j";
62 simple-audio-card,format = "left_j";
63 simple-audio-card,bitclock-master = <&sndcodec>; 48 simple-audio-card,bitclock-master = <&sndcodec>;
64 simple-audio-card,frame-master = <&sndcodec>; 49 simple-audio-card,frame-master = <&sndcodec>;
65 50
66 simple-audio-card,convert-rate = <48000>; /* see audio_clk_a */ 51 simple-audio-card,convert-rate = <48000>;
67 52
68 simple-audio-card,prefix = "ak4642"; 53 simple-audio-card,prefix = "ak4642";
69 simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback", 54 simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
@@ -79,20 +64,18 @@ sound {
79 }; 64 };
80}; 65};
81 66
82Example 2. 2 CPU 1 Codec 67Example 2. 2 CPU 1 Codec (Mixing)
83 68
84sound { 69sound {
85 compatible = "renesas,rsrc-card"; 70 compatible = "simple-scu-audio-card";
86
87 card-name = "rsnd-ak4643";
88 format = "left_j";
89 bitclock-master = <&dpcmcpu>;
90 frame-master = <&dpcmcpu>;
91 71
92 convert-rate = <48000>; /* see audio_clk_a */ 72 simple-audio-card,name = "rsnd-ak4643";
73 simple-audio-card,format = "left_j";
74 simple-audio-card,bitclock-master = <&dpcmcpu>;
75 simple-audio-card,frame-master = <&dpcmcpu>;
93 76
94 audio-prefix = "ak4642"; 77 simple-audio-card,prefix = "ak4642";
95 audio-routing = "ak4642 Playback", "DAI0 Playback", 78 simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
96 "ak4642 Playback", "DAI1 Playback"; 79 "ak4642 Playback", "DAI1 Playback";
97 80
98 dpcmcpu: cpu@0 { 81 dpcmcpu: cpu@0 {
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 28d5f53bc631..cb1c49ae3b88 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1601,6 +1601,7 @@ int of_phandle_iterator_init(struct of_phandle_iterator *it,
1601 1601
1602 return 0; 1602 return 0;
1603} 1603}
1604EXPORT_SYMBOL_GPL(of_phandle_iterator_init);
1604 1605
1605int of_phandle_iterator_next(struct of_phandle_iterator *it) 1606int of_phandle_iterator_next(struct of_phandle_iterator *it)
1606{ 1607{
@@ -1670,6 +1671,7 @@ err:
1670 1671
1671 return -EINVAL; 1672 return -EINVAL;
1672} 1673}
1674EXPORT_SYMBOL_GPL(of_phandle_iterator_next);
1673 1675
1674int of_phandle_iterator_args(struct of_phandle_iterator *it, 1676int of_phandle_iterator_args(struct of_phandle_iterator *it,
1675 uint32_t *args, 1677 uint32_t *args,
@@ -2485,6 +2487,41 @@ struct device_node *of_graph_get_endpoint_by_regs(
2485EXPORT_SYMBOL(of_graph_get_endpoint_by_regs); 2487EXPORT_SYMBOL(of_graph_get_endpoint_by_regs);
2486 2488
2487/** 2489/**
2490 * of_graph_get_remote_endpoint() - get remote endpoint node
2491 * @node: pointer to a local endpoint device_node
2492 *
2493 * Return: Remote endpoint node associated with remote endpoint node linked
2494 * to @node. Use of_node_put() on it when done.
2495 */
2496struct device_node *of_graph_get_remote_endpoint(const struct device_node *node)
2497{
2498 /* Get remote endpoint node. */
2499 return of_parse_phandle(node, "remote-endpoint", 0);
2500}
2501EXPORT_SYMBOL(of_graph_get_remote_endpoint);
2502
2503/**
2504 * of_graph_get_port_parent() - get port's parent node
2505 * @node: pointer to a local endpoint device_node
2506 *
2507 * Return: device node associated with endpoint node linked
2508 * to @node. Use of_node_put() on it when done.
2509 */
2510struct device_node *of_graph_get_port_parent(struct device_node *node)
2511{
2512 unsigned int depth;
2513
2514 /* Walk 3 levels up only if there is 'ports' node. */
2515 for (depth = 3; depth && node; depth--) {
2516 node = of_get_next_parent(node);
2517 if (depth == 2 && of_node_cmp(node->name, "ports"))
2518 break;
2519 }
2520 return node;
2521}
2522EXPORT_SYMBOL(of_graph_get_port_parent);
2523
2524/**
2488 * of_graph_get_remote_port_parent() - get remote port's parent node 2525 * of_graph_get_remote_port_parent() - get remote port's parent node
2489 * @node: pointer to a local endpoint device_node 2526 * @node: pointer to a local endpoint device_node
2490 * 2527 *
@@ -2495,18 +2532,11 @@ struct device_node *of_graph_get_remote_port_parent(
2495 const struct device_node *node) 2532 const struct device_node *node)
2496{ 2533{
2497 struct device_node *np; 2534 struct device_node *np;
2498 unsigned int depth;
2499 2535
2500 /* Get remote endpoint node. */ 2536 /* Get remote endpoint node. */
2501 np = of_parse_phandle(node, "remote-endpoint", 0); 2537 np = of_graph_get_remote_endpoint(node);
2502 2538
2503 /* Walk 3 levels up only if there is 'ports' node. */ 2539 return of_graph_get_port_parent(np);
2504 for (depth = 3; depth && np; depth--) {
2505 np = of_get_next_parent(np);
2506 if (depth == 2 && of_node_cmp(np->name, "ports"))
2507 break;
2508 }
2509 return np;
2510} 2540}
2511EXPORT_SYMBOL(of_graph_get_remote_port_parent); 2541EXPORT_SYMBOL(of_graph_get_remote_port_parent);
2512 2542
@@ -2522,13 +2552,25 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node)
2522 struct device_node *np; 2552 struct device_node *np;
2523 2553
2524 /* Get remote endpoint node. */ 2554 /* Get remote endpoint node. */
2525 np = of_parse_phandle(node, "remote-endpoint", 0); 2555 np = of_graph_get_remote_endpoint(node);
2526 if (!np) 2556 if (!np)
2527 return NULL; 2557 return NULL;
2528 return of_get_next_parent(np); 2558 return of_get_next_parent(np);
2529} 2559}
2530EXPORT_SYMBOL(of_graph_get_remote_port); 2560EXPORT_SYMBOL(of_graph_get_remote_port);
2531 2561
2562int of_graph_get_endpoint_count(const struct device_node *np)
2563{
2564 struct device_node *endpoint;
2565 int num = 0;
2566
2567 for_each_endpoint_of_node(np, endpoint)
2568 num++;
2569
2570 return num;
2571}
2572EXPORT_SYMBOL(of_graph_get_endpoint_count);
2573
2532/** 2574/**
2533 * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint 2575 * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
2534 * @node: pointer to parent device_node containing graph port/endpoint 2576 * @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 abdb02eaef06..3e058f05ab04 100644
--- a/include/linux/of_graph.h
+++ b/include/linux/of_graph.h
@@ -43,11 +43,15 @@ struct of_endpoint {
43#ifdef CONFIG_OF 43#ifdef CONFIG_OF
44int of_graph_parse_endpoint(const struct device_node *node, 44int of_graph_parse_endpoint(const struct device_node *node,
45 struct of_endpoint *endpoint); 45 struct of_endpoint *endpoint);
46int of_graph_get_endpoint_count(const struct device_node *np);
46struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id); 47struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id);
47struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, 48struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
48 struct device_node *previous); 49 struct device_node *previous);
49struct device_node *of_graph_get_endpoint_by_regs( 50struct device_node *of_graph_get_endpoint_by_regs(
50 const struct device_node *parent, int port_reg, int reg); 51 const struct device_node *parent, int port_reg, int reg);
52struct device_node *of_graph_get_remote_endpoint(
53 const struct device_node *node);
54struct device_node *of_graph_get_port_parent(struct device_node *node);
51struct device_node *of_graph_get_remote_port_parent( 55struct device_node *of_graph_get_remote_port_parent(
52 const struct device_node *node); 56 const struct device_node *node);
53struct device_node *of_graph_get_remote_port(const struct device_node *node); 57struct device_node *of_graph_get_remote_port(const struct device_node *node);
@@ -61,6 +65,11 @@ static inline int of_graph_parse_endpoint(const struct device_node *node,
61 return -ENOSYS; 65 return -ENOSYS;
62} 66}
63 67
68static inline int of_graph_get_endpoint_count(const struct device_node *np)
69{
70 return 0;
71}
72
64static inline struct device_node *of_graph_get_port_by_id( 73static inline struct device_node *of_graph_get_port_by_id(
65 struct device_node *node, u32 id) 74 struct device_node *node, u32 id)
66{ 75{
@@ -80,6 +89,18 @@ static inline struct device_node *of_graph_get_endpoint_by_regs(
80 return NULL; 89 return NULL;
81} 90}
82 91
92static inline struct device_node *of_graph_get_remote_endpoint(
93 const struct device_node *node)
94{
95 return NULL;
96}
97
98static inline struct device_node *of_graph_get_port_parent(
99 struct device_node *node)
100{
101 return NULL;
102}
103
83static inline struct device_node *of_graph_get_remote_port_parent( 104static inline struct device_node *of_graph_get_remote_port_parent(
84 const struct device_node *node) 105 const struct device_node *node)
85{ 106{
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,
60 const char *cells_name, 60 const char *cells_name,
61 int *is_single_links); 61 int *is_single_links);
62 62
63#define asoc_simple_card_parse_graph_cpu(ep, dai_link) \
64 asoc_simple_card_parse_graph_dai(ep, &dai_link->cpu_of_node, \
65 &dai_link->cpu_dai_name)
66#define asoc_simple_card_parse_graph_codec(ep, dai_link) \
67 asoc_simple_card_parse_graph_dai(ep, &dai_link->codec_of_node, \
68 &dai_link->codec_dai_name)
69int asoc_simple_card_parse_graph_dai(struct device_node *ep,
70 struct device_node **endpoint_np,
71 const char **dai_name);
72
63int asoc_simple_card_init_dai(struct snd_soc_dai *dai, 73int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
64 struct asoc_simple_dai *simple_dai); 74 struct asoc_simple_dai *simple_dai);
65 75
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 5170fd81e1fd..9c94b97c17f8 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -803,6 +803,8 @@ struct snd_soc_component_driver {
803 int (*of_xlate_dai_name)(struct snd_soc_component *component, 803 int (*of_xlate_dai_name)(struct snd_soc_component *component,
804 struct of_phandle_args *args, 804 struct of_phandle_args *args,
805 const char **dai_name); 805 const char **dai_name);
806 int (*of_xlate_dai_id)(struct snd_soc_component *comment,
807 struct device_node *endpoint);
806 void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type, 808 void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type,
807 int subseq); 809 int subseq);
808 int (*stream_event)(struct snd_soc_component *, int event); 810 int (*stream_event)(struct snd_soc_component *, int event);
@@ -1676,6 +1678,7 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
1676 const char *prefix, 1678 const char *prefix,
1677 struct device_node **bitclkmaster, 1679 struct device_node **bitclkmaster,
1678 struct device_node **framemaster); 1680 struct device_node **framemaster);
1681int snd_soc_get_dai_id(struct device_node *ep);
1679int snd_soc_get_dai_name(struct of_phandle_args *args, 1682int snd_soc_get_dai_name(struct of_phandle_args *args,
1680 const char **dai_name); 1683 const char **dai_name);
1681int snd_soc_of_get_dai_name(struct device_node *of_node, 1684int snd_soc_of_get_dai_name(struct device_node *of_node,
diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig
index d023959b8cd6..121a48e8bb7d 100644
--- a/sound/soc/generic/Kconfig
+++ b/sound/soc/generic/Kconfig
@@ -14,3 +14,11 @@ config SND_SIMPLE_SCU_CARD
14 help 14 help
15 This option enables generic simple SCU sound card support. 15 This option enables generic simple SCU sound card support.
16 It supports DPCM of multi CPU single Codec system. 16 It supports DPCM of multi CPU single Codec system.
17
18config SND_AUDIO_GRAPH_CARD
19 tristate "ASoC Audio Graph sound card support"
20 depends on OF
21 select SND_SIMPLE_CARD_UTILS
22 help
23 This option enables generic simple simple sound card support
24 with OF-graph DT bindings.
diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile
index ee750f3023ba..670068f257b9 100644
--- a/sound/soc/generic/Makefile
+++ b/sound/soc/generic/Makefile
@@ -1,7 +1,9 @@
1snd-soc-simple-card-utils-objs := simple-card-utils.o 1snd-soc-simple-card-utils-objs := simple-card-utils.o
2snd-soc-simple-card-objs := simple-card.o 2snd-soc-simple-card-objs := simple-card.o
3snd-soc-simple-scu-card-objs := simple-scu-card.o 3snd-soc-simple-scu-card-objs := simple-scu-card.o
4snd-soc-audio-graph-card-objs := audio-graph-card.o
4 5
5obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o 6obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o
6obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o 7obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o
7obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o 8obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o
9obj-$(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 000000000000..93c167a91d2d
--- /dev/null
+++ b/sound/soc/generic/audio-graph-card.c
@@ -0,0 +1,310 @@
1/*
2 * ASoC audio graph sound card support
3 *
4 * Copyright (C) 2016 Renesas Solutions Corp.
5 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
6 *
7 * based on ${LINUX}/sound/soc/generic/simple-card.c
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13#include <linux/clk.h>
14#include <linux/device.h>
15#include <linux/gpio.h>
16#include <linux/module.h>
17#include <linux/of.h>
18#include <linux/of_device.h>
19#include <linux/of_gpio.h>
20#include <linux/of_graph.h>
21#include <linux/platform_device.h>
22#include <linux/string.h>
23#include <sound/jack.h>
24#include <sound/simple_card_utils.h>
25
26struct graph_card_data {
27 struct snd_soc_card snd_card;
28 struct graph_dai_props {
29 struct asoc_simple_dai cpu_dai;
30 struct asoc_simple_dai codec_dai;
31 } *dai_props;
32 struct snd_soc_dai_link *dai_link;
33};
34
35#define graph_priv_to_card(priv) (&(priv)->snd_card)
36#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i))
37#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev)
38#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i))
39
40static int asoc_graph_card_startup(struct snd_pcm_substream *substream)
41{
42 struct snd_soc_pcm_runtime *rtd = substream->private_data;
43 struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
44 struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
45 int ret;
46
47 ret = clk_prepare_enable(dai_props->cpu_dai.clk);
48 if (ret)
49 return ret;
50
51 ret = clk_prepare_enable(dai_props->codec_dai.clk);
52 if (ret)
53 clk_disable_unprepare(dai_props->cpu_dai.clk);
54
55 return ret;
56}
57
58static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream)
59{
60 struct snd_soc_pcm_runtime *rtd = substream->private_data;
61 struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
62 struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num);
63
64 clk_disable_unprepare(dai_props->cpu_dai.clk);
65
66 clk_disable_unprepare(dai_props->codec_dai.clk);
67}
68
69static struct snd_soc_ops asoc_graph_card_ops = {
70 .startup = asoc_graph_card_startup,
71 .shutdown = asoc_graph_card_shutdown,
72};
73
74static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd)
75{
76 struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card);
77 struct snd_soc_dai *codec = rtd->codec_dai;
78 struct snd_soc_dai *cpu = rtd->cpu_dai;
79 struct graph_dai_props *dai_props =
80 graph_priv_to_props(priv, rtd->num);
81 int ret;
82
83 ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai);
84 if (ret < 0)
85 return ret;
86
87 ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai);
88 if (ret < 0)
89 return ret;
90
91 return 0;
92}
93
94static int asoc_graph_card_dai_link_of(struct device_node *cpu_port,
95 struct graph_card_data *priv,
96 int idx)
97{
98 struct device *dev = graph_priv_to_dev(priv);
99 struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx);
100 struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx);
101 struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
102 struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
103 struct snd_soc_card *card = graph_priv_to_card(priv);
104 struct device_node *cpu_ep = of_get_next_child(cpu_port, NULL);
105 struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep);
106 struct device_node *rcpu_ep = of_graph_get_remote_endpoint(codec_ep);
107 int ret;
108
109 if (rcpu_ep != cpu_ep) {
110 dev_err(dev, "remote-endpoint mismatch (%s/%s/%s)\n",
111 cpu_ep->name, codec_ep->name, rcpu_ep->name);
112 ret = -EINVAL;
113 goto dai_link_of_err;
114 }
115
116 ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep,
117 NULL, &dai_link->dai_fmt);
118 if (ret < 0)
119 goto dai_link_of_err;
120
121 /*
122 * we need to consider "mclk-fs" around here
123 * see simple-card
124 */
125
126 ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link);
127 if (ret < 0)
128 goto dai_link_of_err;
129
130 ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link);
131 if (ret < 0)
132 goto dai_link_of_err;
133
134 ret = snd_soc_of_parse_tdm_slot(cpu_ep,
135 &cpu_dai->tx_slot_mask,
136 &cpu_dai->rx_slot_mask,
137 &cpu_dai->slots,
138 &cpu_dai->slot_width);
139 if (ret < 0)
140 goto dai_link_of_err;
141
142 ret = snd_soc_of_parse_tdm_slot(codec_ep,
143 &codec_dai->tx_slot_mask,
144 &codec_dai->rx_slot_mask,
145 &codec_dai->slots,
146 &codec_dai->slot_width);
147 if (ret < 0)
148 goto dai_link_of_err;
149
150 ret = asoc_simple_card_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai);
151 if (ret < 0)
152 goto dai_link_of_err;
153
154 ret = asoc_simple_card_parse_clk_codec(dev, codec_ep, dai_link, codec_dai);
155 if (ret < 0)
156 goto dai_link_of_err;
157
158 ret = asoc_simple_card_canonicalize_dailink(dai_link);
159 if (ret < 0)
160 goto dai_link_of_err;
161
162 ret = asoc_simple_card_set_dailink_name(dev, dai_link,
163 "%s-%s",
164 dai_link->cpu_dai_name,
165 dai_link->codec_dai_name);
166 if (ret < 0)
167 goto dai_link_of_err;
168
169 dai_link->ops = &asoc_graph_card_ops;
170 dai_link->init = asoc_graph_card_dai_init;
171
172 dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
173 dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt);
174 dev_dbg(dev, "\tcpu : %s / %d\n",
175 dai_link->cpu_dai_name,
176 cpu_dai->sysclk);
177 dev_dbg(dev, "\tcodec : %s / %d\n",
178 dai_link->codec_dai_name,
179 codec_dai->sysclk);
180
181 asoc_simple_card_canonicalize_cpu(dai_link,
182 card->num_links == 1);
183
184dai_link_of_err:
185 of_node_put(cpu_ep);
186 of_node_put(rcpu_ep);
187 of_node_put(codec_ep);
188
189 return ret;
190}
191
192static int asoc_graph_card_parse_of(struct graph_card_data *priv)
193{
194 struct of_phandle_iterator it;
195 struct device *dev = graph_priv_to_dev(priv);
196 struct snd_soc_card *card = graph_priv_to_card(priv);
197 struct device_node *node = dev->of_node;
198 int rc, idx = 0;
199 int ret;
200
201 /*
202 * we need to consider "widgets", "routing", "mclk-fs" around here
203 * see simple-card
204 */
205
206 of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
207 ret = asoc_graph_card_dai_link_of(it.node, priv, idx++);
208 of_node_put(it.node);
209 if (ret < 0)
210 return ret;
211 }
212
213 return asoc_simple_card_parse_card_name(card, NULL);
214}
215
216static int asoc_graph_get_dais_count(struct device *dev)
217{
218 struct of_phandle_iterator it;
219 struct device_node *node = dev->of_node;
220 int count = 0;
221 int rc;
222
223 of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
224 count++;
225 of_node_put(it.node);
226 }
227
228 return count;
229}
230
231static int asoc_graph_card_probe(struct platform_device *pdev)
232{
233 struct graph_card_data *priv;
234 struct snd_soc_dai_link *dai_link;
235 struct graph_dai_props *dai_props;
236 struct device *dev = &pdev->dev;
237 struct snd_soc_card *card;
238 int num, ret;
239
240 /* Allocate the private data and the DAI link array */
241 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
242 if (!priv)
243 return -ENOMEM;
244
245 num = asoc_graph_get_dais_count(dev);
246 if (num == 0)
247 return -EINVAL;
248
249 dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL);
250 dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL);
251 if (!dai_props || !dai_link)
252 return -ENOMEM;
253
254 priv->dai_props = dai_props;
255 priv->dai_link = dai_link;
256
257 /* Init snd_soc_card */
258 card = graph_priv_to_card(priv);
259 card->owner = THIS_MODULE;
260 card->dev = dev;
261 card->dai_link = dai_link;
262 card->num_links = num;
263
264 ret = asoc_graph_card_parse_of(priv);
265 if (ret < 0) {
266 if (ret != -EPROBE_DEFER)
267 dev_err(dev, "parse error %d\n", ret);
268 goto err;
269 }
270
271 snd_soc_card_set_drvdata(card, priv);
272
273 ret = devm_snd_soc_register_card(dev, card);
274 if (ret < 0)
275 goto err;
276
277 return 0;
278err:
279 asoc_simple_card_clean_reference(card);
280
281 return ret;
282}
283
284static int asoc_graph_card_remove(struct platform_device *pdev)
285{
286 struct snd_soc_card *card = platform_get_drvdata(pdev);
287
288 return asoc_simple_card_clean_reference(card);
289}
290
291static const struct of_device_id asoc_graph_of_match[] = {
292 { .compatible = "audio-graph-card", },
293 {},
294};
295MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
296
297static struct platform_driver asoc_graph_card = {
298 .driver = {
299 .name = "asoc-audio-graph-card",
300 .of_match_table = asoc_graph_of_match,
301 },
302 .probe = asoc_graph_card_probe,
303 .remove = asoc_graph_card_remove,
304};
305module_platform_driver(asoc_graph_card);
306
307MODULE_ALIAS("platform:asoc-audio-graph-card");
308MODULE_LICENSE("GPL v2");
309MODULE_DESCRIPTION("ASoC Audio Graph Sound Card");
310MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 343b291fc372..fe726e83d0bd 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -10,6 +10,7 @@
10#include <linux/clk.h> 10#include <linux/clk.h>
11#include <linux/module.h> 11#include <linux/module.h>
12#include <linux/of.h> 12#include <linux/of.h>
13#include <linux/of_graph.h>
13#include <sound/simple_card_utils.h> 14#include <sound/simple_card_utils.h>
14 15
15int asoc_simple_card_parse_daifmt(struct device *dev, 16int asoc_simple_card_parse_daifmt(struct device *dev,
@@ -81,15 +82,21 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name);
81int asoc_simple_card_parse_card_name(struct snd_soc_card *card, 82int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
82 char *prefix) 83 char *prefix)
83{ 84{
84 char prop[128];
85 int ret; 85 int ret;
86 86
87 snprintf(prop, sizeof(prop), "%sname", prefix); 87 if (!prefix)
88 prefix = "";
88 89
89 /* Parse the card name from DT */ 90 /* Parse the card name from DT */
90 ret = snd_soc_of_parse_card_name(card, prop); 91 ret = snd_soc_of_parse_card_name(card, "label");
91 if (ret < 0) 92 if (ret < 0) {
92 return ret; 93 char prop[128];
94
95 snprintf(prop, sizeof(prop), "%sname", prefix);
96 ret = snd_soc_of_parse_card_name(card, prop);
97 if (ret < 0)
98 return ret;
99 }
93 100
94 if (!card->name && card->dai_link) 101 if (!card->name && card->dai_link)
95 card->name = card->dai_link->name; 102 card->name = card->dai_link->name;
@@ -165,6 +172,71 @@ int asoc_simple_card_parse_dai(struct device_node *node,
165} 172}
166EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai); 173EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai);
167 174
175static int asoc_simple_card_get_dai_id(struct device_node *ep)
176{
177 struct device_node *node;
178 struct device_node *endpoint;
179 int i, id;
180 int ret;
181
182 ret = snd_soc_get_dai_id(ep);
183 if (ret != -ENOTSUPP)
184 return ret;
185
186 node = of_graph_get_port_parent(ep);
187
188 /*
189 * Non HDMI sound case, counting port/endpoint on its DT
190 * is enough. Let's count it.
191 */
192 i = 0;
193 id = -1;
194 for_each_endpoint_of_node(node, endpoint) {
195 if (endpoint == ep)
196 id = i;
197 i++;
198 }
199 if (id < 0)
200 return -ENODEV;
201
202 return id;
203}
204
205int asoc_simple_card_parse_graph_dai(struct device_node *ep,
206 struct device_node **dai_of_node,
207 const char **dai_name)
208{
209 struct device_node *node;
210 struct of_phandle_args args;
211 int ret;
212
213 if (!ep)
214 return 0;
215 if (!dai_name)
216 return 0;
217
218 /*
219 * of_graph_get_port_parent() will call
220 * of_node_put(). So, call of_node_get() here
221 */
222 of_node_get(ep);
223 node = of_graph_get_port_parent(ep);
224
225 /* Get dai->name */
226 args.np = node;
227 args.args[0] = asoc_simple_card_get_dai_id(ep);
228 args.args_count = (of_graph_get_endpoint_count(node) > 1);
229
230 ret = snd_soc_get_dai_name(&args, dai_name);
231 if (ret < 0)
232 return ret;
233
234 *dai_of_node = node;
235
236 return 0;
237}
238EXPORT_SYMBOL_GPL(asoc_simple_card_parse_graph_dai);
239
168int asoc_simple_card_init_dai(struct snd_soc_dai *dai, 240int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
169 struct asoc_simple_dai *simple_dai) 241 struct asoc_simple_dai *simple_dai)
170{ 242{
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index aae099c0e502..9ba183781017 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -34,6 +34,7 @@
34#include <linux/ctype.h> 34#include <linux/ctype.h>
35#include <linux/slab.h> 35#include <linux/slab.h>
36#include <linux/of.h> 36#include <linux/of.h>
37#include <linux/of_graph.h>
37#include <linux/dmi.h> 38#include <linux/dmi.h>
38#include <sound/core.h> 39#include <sound/core.h>
39#include <sound/jack.h> 40#include <sound/jack.h>
@@ -3960,11 +3961,15 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
3960 prefix = ""; 3961 prefix = "";
3961 3962
3962 /* 3963 /*
3963 * check "[prefix]format = xxx" 3964 * check "dai-format = xxx"
3965 * or "[prefix]format = xxx"
3964 * SND_SOC_DAIFMT_FORMAT_MASK area 3966 * SND_SOC_DAIFMT_FORMAT_MASK area
3965 */ 3967 */
3966 snprintf(prop, sizeof(prop), "%sformat", prefix); 3968 ret = of_property_read_string(np, "dai-format", &str);
3967 ret = of_property_read_string(np, prop, &str); 3969 if (ret < 0) {
3970 snprintf(prop, sizeof(prop), "%sformat", prefix);
3971 ret = of_property_read_string(np, prop, &str);
3972 }
3968 if (ret == 0) { 3973 if (ret == 0) {
3969 for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) { 3974 for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) {
3970 if (strcmp(str, of_fmt_table[i].name) == 0) { 3975 if (strcmp(str, of_fmt_table[i].name) == 0) {
@@ -4044,6 +4049,42 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
4044} 4049}
4045EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); 4050EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt);
4046 4051
4052int snd_soc_get_dai_id(struct device_node *ep)
4053{
4054 struct snd_soc_component *pos;
4055 struct device_node *node;
4056 int ret;
4057
4058 node = of_graph_get_port_parent(ep);
4059
4060 /*
4061 * For example HDMI case, HDMI has video/sound port,
4062 * but ALSA SoC needs sound port number only.
4063 * Thus counting HDMI DT port/endpoint doesn't work.
4064 * Then, it should have .of_xlate_dai_id
4065 */
4066 ret = -ENOTSUPP;
4067 mutex_lock(&client_mutex);
4068 list_for_each_entry(pos, &component_list, list) {
4069 struct device_node *component_of_node = pos->dev->of_node;
4070
4071 if (!component_of_node && pos->dev->parent)
4072 component_of_node = pos->dev->parent->of_node;
4073
4074 if (component_of_node != node)
4075 continue;
4076
4077 if (pos->driver->of_xlate_dai_id)
4078 ret = pos->driver->of_xlate_dai_id(pos, ep);
4079
4080 break;
4081 }
4082 mutex_unlock(&client_mutex);
4083
4084 return ret;
4085}
4086EXPORT_SYMBOL_GPL(snd_soc_get_dai_id);
4087
4047int snd_soc_get_dai_name(struct of_phandle_args *args, 4088int snd_soc_get_dai_name(struct of_phandle_args *args,
4048 const char **dai_name) 4089 const char **dai_name)
4049{ 4090{