diff options
26 files changed, 1546 insertions, 215 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 @@ | |||
1 | Audio Graph Card: | ||
2 | |||
3 | Audio Graph Card specifies audio DAI connections of SoC <-> codec. | ||
4 | It is based on common bindings for device graphs. | ||
5 | see ${LINUX}/Documentation/devicetree/bindings/graph.txt | ||
6 | |||
7 | Basically, Audio Graph Card property is same as Simple Card. | ||
8 | see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt | ||
9 | |||
10 | Below 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 | |||
22 | Required properties: | ||
23 | |||
24 | - compatible : "audio-graph-card"; | ||
25 | - dais : list of CPU DAI port{s} | ||
26 | |||
27 | Example: 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 | |||
56 | Example: 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/audio-graph-scu-card.txt b/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt new file mode 100644 index 000000000000..b63c5594bbb3 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt | |||
@@ -0,0 +1,117 @@ | |||
1 | Audio-Graph-SCU-Card: | ||
2 | |||
3 | Audio-Graph-SCU-Card is "Audio-Graph-Card" + "ALSA DPCM". | ||
4 | |||
5 | It is based on common bindings for device graphs. | ||
6 | see ${LINUX}/Documentation/devicetree/bindings/graph.txt | ||
7 | |||
8 | Basically, Audio-Graph-SCU-Card property is same as | ||
9 | Simple-Card / Simple-SCU-Card / Audio-Graph-Card. | ||
10 | see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt | ||
11 | ${LINUX}/Documentation/devicetree/bindings/sound/simple-scu-card.txt | ||
12 | ${LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt | ||
13 | |||
14 | Below are same as Simple-Card / Audio-Graph-Card. | ||
15 | |||
16 | - label | ||
17 | - dai-format | ||
18 | - frame-master | ||
19 | - bitclock-master | ||
20 | - bitclock-inversion | ||
21 | - frame-inversion | ||
22 | - dai-tdm-slot-num | ||
23 | - dai-tdm-slot-width | ||
24 | - clocks / system-clock-frequency | ||
25 | |||
26 | Below are same as Simple-SCU-Card. | ||
27 | |||
28 | - convert-rate | ||
29 | - convert-channels | ||
30 | - prefix | ||
31 | - routing | ||
32 | |||
33 | Required properties: | ||
34 | |||
35 | - compatible : "audio-graph-scu-card"; | ||
36 | - dais : list of CPU DAI port{s} | ||
37 | |||
38 | Example 1. Sampling Rate Conversion | ||
39 | |||
40 | sound_card { | ||
41 | compatible = "audio-graph-scu-card"; | ||
42 | |||
43 | label = "sound-card"; | ||
44 | prefix = "codec"; | ||
45 | routing = "codec Playback", "DAI0 Playback", | ||
46 | "codec Playback", "DAI1 Playback"; | ||
47 | convert-rate = <48000>; | ||
48 | |||
49 | dais = <&cpu_port>; | ||
50 | }; | ||
51 | |||
52 | audio-codec { | ||
53 | ... | ||
54 | |||
55 | port { | ||
56 | codec_endpoint: endpoint { | ||
57 | remote-endpoint = <&cpu_endpoint>; | ||
58 | }; | ||
59 | }; | ||
60 | }; | ||
61 | |||
62 | dai-controller { | ||
63 | ... | ||
64 | cpu_port: port { | ||
65 | cpu_endpoint: endpoint { | ||
66 | remote-endpoint = <&codec_endpoint>; | ||
67 | |||
68 | dai-format = "left_j"; | ||
69 | ... | ||
70 | }; | ||
71 | }; | ||
72 | }; | ||
73 | |||
74 | Example 2. 2 CPU 1 Codec (Mixing) | ||
75 | |||
76 | sound_card { | ||
77 | compatible = "audio-graph-scu-card"; | ||
78 | |||
79 | label = "sound-card"; | ||
80 | prefix = "codec"; | ||
81 | routing = "codec Playback", "DAI0 Playback", | ||
82 | "codec Playback", "DAI1 Playback"; | ||
83 | convert-rate = <48000>; | ||
84 | |||
85 | dais = <&cpu_port0 | ||
86 | &cpu_port1>; | ||
87 | }; | ||
88 | |||
89 | audio-codec { | ||
90 | ... | ||
91 | |||
92 | port { | ||
93 | codec_endpoint: endpoint { | ||
94 | remote-endpoint = <&cpu_endpoint0>; | ||
95 | }; | ||
96 | }; | ||
97 | }; | ||
98 | |||
99 | dai-controller { | ||
100 | ... | ||
101 | ports { | ||
102 | cpu_port0: port { | ||
103 | cpu_endpoint0: endpoint { | ||
104 | remote-endpoint = <&codec_endpoint>; | ||
105 | |||
106 | dai-format = "left_j"; | ||
107 | ... | ||
108 | }; | ||
109 | }; | ||
110 | cpu_port1: port { | ||
111 | cpu_endpoint1: endpoint { | ||
112 | dai-format = "left_j"; | ||
113 | ... | ||
114 | }; | ||
115 | }; | ||
116 | }; | ||
117 | }; | ||
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index 15a7316e4c91..7246bb268bf9 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt | |||
@@ -83,11 +83,11 @@ SRC can convert [xx]Hz to [yy]Hz. Then, it has below 2 modes | |||
83 | ** Asynchronous mode | 83 | ** Asynchronous mode |
84 | ------------------ | 84 | ------------------ |
85 | 85 | ||
86 | You need to use "renesas,rsrc-card" sound card for it. | 86 | You need to use "simple-scu-audio-card" sound card for it. |
87 | example) | 87 | example) |
88 | 88 | ||
89 | sound { | 89 | sound { |
90 | compatible = "renesas,rsrc-card"; | 90 | compatible = "simple-scu-audio-card"; |
91 | ... | 91 | ... |
92 | /* | 92 | /* |
93 | * SRC Asynchronous mode setting | 93 | * SRC Asynchronous mode setting |
@@ -97,12 +97,12 @@ example) | |||
97 | * Inputed 48kHz data will be converted to | 97 | * Inputed 48kHz data will be converted to |
98 | * system specified Hz | 98 | * system specified Hz |
99 | */ | 99 | */ |
100 | convert-rate = <48000>; | 100 | simple-audio-card,convert-rate = <48000>; |
101 | ... | 101 | ... |
102 | cpu { | 102 | simple-audio-card,cpu { |
103 | sound-dai = <&rcar_sound>; | 103 | sound-dai = <&rcar_sound>; |
104 | }; | 104 | }; |
105 | codec { | 105 | simple-audio-card,codec { |
106 | ... | 106 | ... |
107 | }; | 107 | }; |
108 | }; | 108 | }; |
@@ -141,23 +141,23 @@ For more detail information, see below | |||
141 | ${LINUX}/sound/soc/sh/rcar/ctu.c | 141 | ${LINUX}/sound/soc/sh/rcar/ctu.c |
142 | - comment of header | 142 | - comment of header |
143 | 143 | ||
144 | You need to use "renesas,rsrc-card" sound card for it. | 144 | You need to use "simple-scu-audio-card" sound card for it. |
145 | example) | 145 | example) |
146 | 146 | ||
147 | sound { | 147 | sound { |
148 | compatible = "renesas,rsrc-card"; | 148 | compatible = "simple-scu-audio-card"; |
149 | ... | 149 | ... |
150 | /* | 150 | /* |
151 | * CTU setting | 151 | * CTU setting |
152 | * All input data will be converted to 2ch | 152 | * All input data will be converted to 2ch |
153 | * as output data | 153 | * as output data |
154 | */ | 154 | */ |
155 | convert-channels = <2>; | 155 | simple-audio-card,convert-channels = <2>; |
156 | ... | 156 | ... |
157 | cpu { | 157 | simple-audio-card,cpu { |
158 | sound-dai = <&rcar_sound>; | 158 | sound-dai = <&rcar_sound>; |
159 | }; | 159 | }; |
160 | codec { | 160 | simple-audio-card,codec { |
161 | ... | 161 | ... |
162 | }; | 162 | }; |
163 | }; | 163 | }; |
@@ -190,22 +190,22 @@ and these sounds will be merged by MIX. | |||
190 | aplay -D plughw:0,0 xxxx.wav & | 190 | aplay -D plughw:0,0 xxxx.wav & |
191 | aplay -D plughw:0,1 yyyy.wav | 191 | aplay -D plughw:0,1 yyyy.wav |
192 | 192 | ||
193 | You need to use "renesas,rsrc-card" sound card for it. | 193 | You need to use "simple-scu-audio-card" sound card for it. |
194 | Ex) | 194 | Ex) |
195 | [MEM] -> [SRC1] -> [CTU02] -+-> [MIX0] -> [DVC0] -> [SSI0] | 195 | [MEM] -> [SRC1] -> [CTU02] -+-> [MIX0] -> [DVC0] -> [SSI0] |
196 | | | 196 | | |
197 | [MEM] -> [SRC2] -> [CTU03] -+ | 197 | [MEM] -> [SRC2] -> [CTU03] -+ |
198 | 198 | ||
199 | sound { | 199 | sound { |
200 | compatible = "renesas,rsrc-card"; | 200 | compatible = "simple-scu-audio-card"; |
201 | ... | 201 | ... |
202 | cpu@0 { | 202 | simple-audio-card,cpu@0 { |
203 | sound-dai = <&rcar_sound 0>; | 203 | sound-dai = <&rcar_sound 0>; |
204 | }; | 204 | }; |
205 | cpu@1 { | 205 | simple-audio-card,cpu@1 { |
206 | sound-dai = <&rcar_sound 1>; | 206 | sound-dai = <&rcar_sound 1>; |
207 | }; | 207 | }; |
208 | codec { | 208 | simple-audio-card,codec { |
209 | ... | 209 | ... |
210 | }; | 210 | }; |
211 | }; | 211 | }; |
@@ -368,6 +368,10 @@ Required properties: | |||
368 | see below for detail. | 368 | see below for detail. |
369 | - #sound-dai-cells : it must be 0 if your system is using single DAI | 369 | - #sound-dai-cells : it must be 0 if your system is using single DAI |
370 | it must be 1 if your system is using multi DAI | 370 | it must be 1 if your system is using multi DAI |
371 | - clocks : References to SSI/SRC/MIX/CTU/DVC/AUDIO_CLK clocks. | ||
372 | - clock-names : List of necessary clock names. | ||
373 | "ssi-all", "ssi.X", "src.X", "mix.X", "ctu.X", | ||
374 | "dvc.X", "clk_a", "clk_b", "clk_c", "clk_i" | ||
371 | 375 | ||
372 | Optional properties: | 376 | Optional properties: |
373 | - #clock-cells : it must be 0 if your system has audio_clkout | 377 | - #clock-cells : it must be 0 if your system has audio_clkout |
@@ -375,6 +379,9 @@ Optional properties: | |||
375 | - clock-frequency : for all audio_clkout0/1/2/3 | 379 | - clock-frequency : for all audio_clkout0/1/2/3 |
376 | - clkout-lr-asynchronous : boolean property. it indicates that audio_clkoutn | 380 | - clkout-lr-asynchronous : boolean property. it indicates that audio_clkoutn |
377 | is asynchronizes with lr-clock. | 381 | is asynchronizes with lr-clock. |
382 | - resets : References to SSI resets. | ||
383 | - reset-names : List of valid reset names. | ||
384 | "ssi-all", "ssi.X" | ||
378 | 385 | ||
379 | SSI subnode properties: | 386 | SSI subnode properties: |
380 | - interrupts : Should contain SSI interrupt for PIO transfer | 387 | - interrupts : Should contain SSI interrupt for PIO transfer |
diff --git a/Documentation/devicetree/bindings/sound/simple-scu-card.txt b/Documentation/devicetree/bindings/sound/simple-scu-card.txt index d6fe47ed09af..327d229a51b2 100644 --- a/Documentation/devicetree/bindings/sound/simple-scu-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-scu-card.txt | |||
@@ -1,35 +1,29 @@ | |||
1 | ASoC simple SCU Sound Card | 1 | ASoC Simple SCU Sound Card |
2 | 2 | ||
3 | Simple-Card specifies audio DAI connections of SoC <-> codec. | 3 | Simple SCU Sound Card is "Simple Sound Card" + "ALSA DPCM". |
4 | For example, you can use this driver if you want to exchange sampling rate convert, | ||
5 | Mixing, etc... | ||
4 | 6 | ||
5 | Required properties: | 7 | Required properties: |
6 | 8 | ||
7 | - compatible : "simple-scu-audio-card" | 9 | - compatible : "simple-scu-audio-card" |
8 | "renesas,rsrc-card" | 10 | "renesas,rsrc-card" |
9 | |||
10 | Optional properties: | 11 | Optional 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 | ||
17 | Optional subnode properties: | 17 | Optional 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,32 +32,23 @@ Optional subnode properties: | |||
38 | 32 | ||
39 | Required CPU/CODEC subnodes properties: | 33 | Required 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 | ||
43 | Optional CPU/CODEC subnodes properties: | 37 | Optional 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 | ||
55 | Example 1. Sampling Rate Covert | 41 | Example 1. Sampling Rate Conversion |
56 | 42 | ||
57 | sound { | 43 | sound { |
58 | compatible = "simple-scu-audio-card"; | 44 | compatible = "simple-scu-audio-card"; |
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 | ||
82 | Example 2. 2 CPU 1 Codec | 67 | Example 2. 2 CPU 1 Codec (Mixing) |
83 | 68 | ||
84 | sound { | 69 | sound { |
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 | } |
1604 | EXPORT_SYMBOL_GPL(of_phandle_iterator_init); | ||
1604 | 1605 | ||
1605 | int of_phandle_iterator_next(struct of_phandle_iterator *it) | 1606 | int 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 | } |
1674 | EXPORT_SYMBOL_GPL(of_phandle_iterator_next); | ||
1673 | 1675 | ||
1674 | int of_phandle_iterator_args(struct of_phandle_iterator *it, | 1676 | int 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( | |||
2485 | EXPORT_SYMBOL(of_graph_get_endpoint_by_regs); | 2487 | EXPORT_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 | */ | ||
2496 | struct 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 | } | ||
2501 | EXPORT_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 | */ | ||
2510 | struct 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 | } | ||
2522 | EXPORT_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 | } |
2511 | EXPORT_SYMBOL(of_graph_get_remote_port_parent); | 2541 | EXPORT_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 | } |
2530 | EXPORT_SYMBOL(of_graph_get_remote_port); | 2560 | EXPORT_SYMBOL(of_graph_get_remote_port); |
2531 | 2561 | ||
2562 | int 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 | } | ||
2572 | EXPORT_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 |
44 | int of_graph_parse_endpoint(const struct device_node *node, | 44 | int of_graph_parse_endpoint(const struct device_node *node, |
45 | struct of_endpoint *endpoint); | 45 | struct of_endpoint *endpoint); |
46 | int of_graph_get_endpoint_count(const struct device_node *np); | ||
46 | struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id); | 47 | struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id); |
47 | struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, | 48 | struct device_node *of_graph_get_next_endpoint(const struct device_node *parent, |
48 | struct device_node *previous); | 49 | struct device_node *previous); |
49 | struct device_node *of_graph_get_endpoint_by_regs( | 50 | struct 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); |
52 | struct device_node *of_graph_get_remote_endpoint( | ||
53 | const struct device_node *node); | ||
54 | struct device_node *of_graph_get_port_parent(struct device_node *node); | ||
51 | struct device_node *of_graph_get_remote_port_parent( | 55 | struct device_node *of_graph_get_remote_port_parent( |
52 | const struct device_node *node); | 56 | const struct device_node *node); |
53 | struct device_node *of_graph_get_remote_port(const struct device_node *node); | 57 | struct 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 | ||
68 | static inline int of_graph_get_endpoint_count(const struct device_node *np) | ||
69 | { | ||
70 | return 0; | ||
71 | } | ||
72 | |||
64 | static inline struct device_node *of_graph_get_port_by_id( | 73 | static 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 | ||
92 | static inline struct device_node *of_graph_get_remote_endpoint( | ||
93 | const struct device_node *node) | ||
94 | { | ||
95 | return NULL; | ||
96 | } | ||
97 | |||
98 | static inline struct device_node *of_graph_get_port_parent( | ||
99 | struct device_node *node) | ||
100 | { | ||
101 | return NULL; | ||
102 | } | ||
103 | |||
83 | static inline struct device_node *of_graph_get_remote_port_parent( | 104 | static 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..108cae459ed0 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h | |||
@@ -35,13 +35,16 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card, | |||
35 | char *prefix); | 35 | char *prefix); |
36 | 36 | ||
37 | #define asoc_simple_card_parse_clk_cpu(dev, node, dai_link, simple_dai) \ | 37 | #define asoc_simple_card_parse_clk_cpu(dev, node, dai_link, simple_dai) \ |
38 | asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai) | 38 | asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai, \ |
39 | dai_link->cpu_dai_name) | ||
39 | #define asoc_simple_card_parse_clk_codec(dev, node, dai_link, simple_dai) \ | 40 | #define asoc_simple_card_parse_clk_codec(dev, node, dai_link, simple_dai) \ |
40 | asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai) | 41 | asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai,\ |
42 | dai_link->codec_dai_name) | ||
41 | int asoc_simple_card_parse_clk(struct device *dev, | 43 | int asoc_simple_card_parse_clk(struct device *dev, |
42 | struct device_node *node, | 44 | struct device_node *node, |
43 | struct device_node *dai_of_node, | 45 | struct device_node *dai_of_node, |
44 | struct asoc_simple_dai *simple_dai); | 46 | struct asoc_simple_dai *simple_dai, |
47 | const char *name); | ||
45 | 48 | ||
46 | #define asoc_simple_card_parse_cpu(node, dai_link, \ | 49 | #define asoc_simple_card_parse_cpu(node, dai_link, \ |
47 | list_name, cells_name, is_single_link) \ | 50 | list_name, cells_name, is_single_link) \ |
@@ -60,6 +63,16 @@ int asoc_simple_card_parse_dai(struct device_node *node, | |||
60 | const char *cells_name, | 63 | const char *cells_name, |
61 | int *is_single_links); | 64 | int *is_single_links); |
62 | 65 | ||
66 | #define asoc_simple_card_parse_graph_cpu(ep, dai_link) \ | ||
67 | asoc_simple_card_parse_graph_dai(ep, &dai_link->cpu_of_node, \ | ||
68 | &dai_link->cpu_dai_name) | ||
69 | #define asoc_simple_card_parse_graph_codec(ep, dai_link) \ | ||
70 | asoc_simple_card_parse_graph_dai(ep, &dai_link->codec_of_node, \ | ||
71 | &dai_link->codec_dai_name) | ||
72 | int asoc_simple_card_parse_graph_dai(struct device_node *ep, | ||
73 | struct device_node **endpoint_np, | ||
74 | const char **dai_name); | ||
75 | |||
63 | int asoc_simple_card_init_dai(struct snd_soc_dai *dai, | 76 | int asoc_simple_card_init_dai(struct snd_soc_dai *dai, |
64 | struct asoc_simple_dai *simple_dai); | 77 | struct asoc_simple_dai *simple_dai); |
65 | 78 | ||
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); |
1681 | int snd_soc_get_dai_id(struct device_node *ep); | ||
1679 | int snd_soc_get_dai_name(struct of_phandle_args *args, | 1682 | int snd_soc_get_dai_name(struct of_phandle_args *args, |
1680 | const char **dai_name); | 1683 | const char **dai_name); |
1681 | int snd_soc_of_get_dai_name(struct device_node *of_node, | 1684 | int 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..c954be0a0f96 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig | |||
@@ -14,3 +14,20 @@ 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 | |||
18 | config 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. | ||
25 | |||
26 | config SND_AUDIO_GRAPH_SCU_CARD | ||
27 | tristate "ASoC Audio Graph SCU sound card support" | ||
28 | depends on OF | ||
29 | select SND_SIMPLE_CARD_UTILS | ||
30 | help | ||
31 | This option enables generic simple SCU sound card support | ||
32 | with OF-graph DT bindings. | ||
33 | It supports DPCM of multi CPU single Codec ststem. | ||
diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile index ee750f3023ba..9e000523a3b4 100644 --- a/sound/soc/generic/Makefile +++ b/sound/soc/generic/Makefile | |||
@@ -1,7 +1,11 @@ | |||
1 | snd-soc-simple-card-utils-objs := simple-card-utils.o | 1 | snd-soc-simple-card-utils-objs := simple-card-utils.o |
2 | snd-soc-simple-card-objs := simple-card.o | 2 | snd-soc-simple-card-objs := simple-card.o |
3 | snd-soc-simple-scu-card-objs := simple-scu-card.o | 3 | snd-soc-simple-scu-card-objs := simple-scu-card.o |
4 | snd-soc-audio-graph-card-objs := audio-graph-card.o | ||
5 | snd-soc-audio-graph-scu-card-objs := audio-graph-scu-card.o | ||
4 | 6 | ||
5 | obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o | 7 | obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) += snd-soc-simple-card-utils.o |
6 | obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o | 8 | obj-$(CONFIG_SND_SIMPLE_CARD) += snd-soc-simple-card.o |
7 | obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o | 9 | obj-$(CONFIG_SND_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o |
10 | obj-$(CONFIG_SND_AUDIO_GRAPH_CARD) += snd-soc-audio-graph-card.o | ||
11 | obj-$(CONFIG_SND_AUDIO_GRAPH_SCU_CARD) += snd-soc-audio-graph-scu-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..0180b286bee3 --- /dev/null +++ b/sound/soc/generic/audio-graph-card.c | |||
@@ -0,0 +1,301 @@ | |||
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 | |||
26 | struct 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 | |||
40 | static 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 | |||
58 | static 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 | |||
69 | static struct snd_soc_ops asoc_graph_card_ops = { | ||
70 | .startup = asoc_graph_card_startup, | ||
71 | .shutdown = asoc_graph_card_shutdown, | ||
72 | }; | ||
73 | |||
74 | static 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 | |||
94 | static 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 | asoc_simple_card_canonicalize_cpu(dai_link, | ||
173 | card->num_links == 1); | ||
174 | |||
175 | dai_link_of_err: | ||
176 | of_node_put(cpu_ep); | ||
177 | of_node_put(rcpu_ep); | ||
178 | of_node_put(codec_ep); | ||
179 | |||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | static int asoc_graph_card_parse_of(struct graph_card_data *priv) | ||
184 | { | ||
185 | struct of_phandle_iterator it; | ||
186 | struct device *dev = graph_priv_to_dev(priv); | ||
187 | struct snd_soc_card *card = graph_priv_to_card(priv); | ||
188 | struct device_node *node = dev->of_node; | ||
189 | int rc, idx = 0; | ||
190 | int ret; | ||
191 | |||
192 | /* | ||
193 | * we need to consider "widgets", "routing", "mclk-fs" around here | ||
194 | * see simple-card | ||
195 | */ | ||
196 | |||
197 | of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { | ||
198 | ret = asoc_graph_card_dai_link_of(it.node, priv, idx++); | ||
199 | of_node_put(it.node); | ||
200 | if (ret < 0) | ||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | return asoc_simple_card_parse_card_name(card, NULL); | ||
205 | } | ||
206 | |||
207 | static int asoc_graph_get_dais_count(struct device *dev) | ||
208 | { | ||
209 | struct of_phandle_iterator it; | ||
210 | struct device_node *node = dev->of_node; | ||
211 | int count = 0; | ||
212 | int rc; | ||
213 | |||
214 | of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { | ||
215 | count++; | ||
216 | of_node_put(it.node); | ||
217 | } | ||
218 | |||
219 | return count; | ||
220 | } | ||
221 | |||
222 | static int asoc_graph_card_probe(struct platform_device *pdev) | ||
223 | { | ||
224 | struct graph_card_data *priv; | ||
225 | struct snd_soc_dai_link *dai_link; | ||
226 | struct graph_dai_props *dai_props; | ||
227 | struct device *dev = &pdev->dev; | ||
228 | struct snd_soc_card *card; | ||
229 | int num, ret; | ||
230 | |||
231 | /* Allocate the private data and the DAI link array */ | ||
232 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
233 | if (!priv) | ||
234 | return -ENOMEM; | ||
235 | |||
236 | num = asoc_graph_get_dais_count(dev); | ||
237 | if (num == 0) | ||
238 | return -EINVAL; | ||
239 | |||
240 | dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); | ||
241 | dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); | ||
242 | if (!dai_props || !dai_link) | ||
243 | return -ENOMEM; | ||
244 | |||
245 | priv->dai_props = dai_props; | ||
246 | priv->dai_link = dai_link; | ||
247 | |||
248 | /* Init snd_soc_card */ | ||
249 | card = graph_priv_to_card(priv); | ||
250 | card->owner = THIS_MODULE; | ||
251 | card->dev = dev; | ||
252 | card->dai_link = dai_link; | ||
253 | card->num_links = num; | ||
254 | |||
255 | ret = asoc_graph_card_parse_of(priv); | ||
256 | if (ret < 0) { | ||
257 | if (ret != -EPROBE_DEFER) | ||
258 | dev_err(dev, "parse error %d\n", ret); | ||
259 | goto err; | ||
260 | } | ||
261 | |||
262 | snd_soc_card_set_drvdata(card, priv); | ||
263 | |||
264 | ret = devm_snd_soc_register_card(dev, card); | ||
265 | if (ret < 0) | ||
266 | goto err; | ||
267 | |||
268 | return 0; | ||
269 | err: | ||
270 | asoc_simple_card_clean_reference(card); | ||
271 | |||
272 | return ret; | ||
273 | } | ||
274 | |||
275 | static int asoc_graph_card_remove(struct platform_device *pdev) | ||
276 | { | ||
277 | struct snd_soc_card *card = platform_get_drvdata(pdev); | ||
278 | |||
279 | return asoc_simple_card_clean_reference(card); | ||
280 | } | ||
281 | |||
282 | static const struct of_device_id asoc_graph_of_match[] = { | ||
283 | { .compatible = "audio-graph-card", }, | ||
284 | {}, | ||
285 | }; | ||
286 | MODULE_DEVICE_TABLE(of, asoc_graph_of_match); | ||
287 | |||
288 | static struct platform_driver asoc_graph_card = { | ||
289 | .driver = { | ||
290 | .name = "asoc-audio-graph-card", | ||
291 | .of_match_table = asoc_graph_of_match, | ||
292 | }, | ||
293 | .probe = asoc_graph_card_probe, | ||
294 | .remove = asoc_graph_card_remove, | ||
295 | }; | ||
296 | module_platform_driver(asoc_graph_card); | ||
297 | |||
298 | MODULE_ALIAS("platform:asoc-audio-graph-card"); | ||
299 | MODULE_LICENSE("GPL v2"); | ||
300 | MODULE_DESCRIPTION("ASoC Audio Graph Sound Card"); | ||
301 | MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); | ||
diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c new file mode 100644 index 000000000000..0066102f5bc4 --- /dev/null +++ b/sound/soc/generic/audio-graph-scu-card.c | |||
@@ -0,0 +1,411 @@ | |||
1 | /* | ||
2 | * ASoC audio graph SCU sound card support | ||
3 | * | ||
4 | * Copyright (C) 2017 Renesas Solutions Corp. | ||
5 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | ||
6 | * | ||
7 | * based on | ||
8 | * ${LINUX}/sound/soc/generic/simple-scu-card.c | ||
9 | * ${LINUX}/sound/soc/generic/audio-graph-card.c | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | #include <linux/clk.h> | ||
16 | #include <linux/device.h> | ||
17 | #include <linux/gpio.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/of_device.h> | ||
21 | #include <linux/of_gpio.h> | ||
22 | #include <linux/of_graph.h> | ||
23 | #include <linux/platform_device.h> | ||
24 | #include <linux/string.h> | ||
25 | #include <sound/jack.h> | ||
26 | #include <sound/simple_card_utils.h> | ||
27 | |||
28 | struct graph_card_data { | ||
29 | struct snd_soc_card snd_card; | ||
30 | struct snd_soc_codec_conf codec_conf; | ||
31 | struct asoc_simple_dai *dai_props; | ||
32 | struct snd_soc_dai_link *dai_link; | ||
33 | u32 convert_rate; | ||
34 | u32 convert_channels; | ||
35 | }; | ||
36 | |||
37 | #define graph_priv_to_card(priv) (&(priv)->snd_card) | ||
38 | #define graph_priv_to_props(priv, i) ((priv)->dai_props + (i)) | ||
39 | #define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) | ||
40 | #define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i)) | ||
41 | |||
42 | static int asoc_graph_card_startup(struct snd_pcm_substream *substream) | ||
43 | { | ||
44 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
45 | struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); | ||
46 | struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, rtd->num); | ||
47 | |||
48 | return clk_prepare_enable(dai_props->clk); | ||
49 | } | ||
50 | |||
51 | static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) | ||
52 | { | ||
53 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
54 | struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); | ||
55 | struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, rtd->num); | ||
56 | |||
57 | clk_disable_unprepare(dai_props->clk); | ||
58 | } | ||
59 | |||
60 | static struct snd_soc_ops asoc_graph_card_ops = { | ||
61 | .startup = asoc_graph_card_startup, | ||
62 | .shutdown = asoc_graph_card_shutdown, | ||
63 | }; | ||
64 | |||
65 | static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) | ||
66 | { | ||
67 | struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); | ||
68 | struct snd_soc_dai *dai; | ||
69 | struct snd_soc_dai_link *dai_link; | ||
70 | struct asoc_simple_dai *dai_props; | ||
71 | int num = rtd->num; | ||
72 | |||
73 | dai_link = graph_priv_to_link(priv, num); | ||
74 | dai_props = graph_priv_to_props(priv, num); | ||
75 | dai = dai_link->dynamic ? | ||
76 | rtd->cpu_dai : | ||
77 | rtd->codec_dai; | ||
78 | |||
79 | return asoc_simple_card_init_dai(dai, dai_props); | ||
80 | } | ||
81 | |||
82 | static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, | ||
83 | struct snd_pcm_hw_params *params) | ||
84 | { | ||
85 | struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); | ||
86 | struct snd_interval *rate = hw_param_interval(params, | ||
87 | SNDRV_PCM_HW_PARAM_RATE); | ||
88 | struct snd_interval *channels = hw_param_interval(params, | ||
89 | SNDRV_PCM_HW_PARAM_CHANNELS); | ||
90 | |||
91 | if (priv->convert_rate) | ||
92 | rate->min = | ||
93 | rate->max = priv->convert_rate; | ||
94 | |||
95 | if (priv->convert_channels) | ||
96 | channels->min = | ||
97 | channels->max = priv->convert_channels; | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static int asoc_graph_card_dai_link_of(struct device_node *ep, | ||
103 | struct graph_card_data *priv, | ||
104 | unsigned int daifmt, | ||
105 | int idx, int is_fe) | ||
106 | { | ||
107 | struct device *dev = graph_priv_to_dev(priv); | ||
108 | struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx); | ||
109 | struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, idx); | ||
110 | struct snd_soc_card *card = graph_priv_to_card(priv); | ||
111 | int ret; | ||
112 | |||
113 | if (is_fe) { | ||
114 | /* BE is dummy */ | ||
115 | dai_link->codec_of_node = NULL; | ||
116 | dai_link->codec_dai_name = "snd-soc-dummy-dai"; | ||
117 | dai_link->codec_name = "snd-soc-dummy"; | ||
118 | |||
119 | /* FE settings */ | ||
120 | dai_link->dynamic = 1; | ||
121 | dai_link->dpcm_merged_format = 1; | ||
122 | |||
123 | ret = asoc_simple_card_parse_graph_cpu(ep, dai_link); | ||
124 | if (ret) | ||
125 | return ret; | ||
126 | |||
127 | ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, dai_props); | ||
128 | if (ret < 0) | ||
129 | return ret; | ||
130 | |||
131 | ret = asoc_simple_card_set_dailink_name(dev, dai_link, | ||
132 | "fe.%s", | ||
133 | dai_link->cpu_dai_name); | ||
134 | if (ret < 0) | ||
135 | return ret; | ||
136 | |||
137 | /* card->num_links includes Codec */ | ||
138 | asoc_simple_card_canonicalize_cpu(dai_link, | ||
139 | (card->num_links - 1) == 1); | ||
140 | } else { | ||
141 | /* FE is dummy */ | ||
142 | dai_link->cpu_of_node = NULL; | ||
143 | dai_link->cpu_dai_name = "snd-soc-dummy-dai"; | ||
144 | dai_link->cpu_name = "snd-soc-dummy"; | ||
145 | |||
146 | /* BE settings */ | ||
147 | dai_link->no_pcm = 1; | ||
148 | dai_link->be_hw_params_fixup = asoc_graph_card_be_hw_params_fixup; | ||
149 | |||
150 | ret = asoc_simple_card_parse_graph_codec(ep, dai_link); | ||
151 | if (ret < 0) | ||
152 | return ret; | ||
153 | |||
154 | ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, dai_props); | ||
155 | if (ret < 0) | ||
156 | return ret; | ||
157 | |||
158 | ret = asoc_simple_card_set_dailink_name(dev, dai_link, | ||
159 | "be.%s", | ||
160 | dai_link->codec_dai_name); | ||
161 | if (ret < 0) | ||
162 | return ret; | ||
163 | |||
164 | snd_soc_of_parse_audio_prefix(card, | ||
165 | &priv->codec_conf, | ||
166 | dai_link->codec_of_node, | ||
167 | "prefix"); | ||
168 | } | ||
169 | |||
170 | ret = snd_soc_of_parse_tdm_slot(ep, | ||
171 | &dai_props->tx_slot_mask, | ||
172 | &dai_props->rx_slot_mask, | ||
173 | &dai_props->slots, | ||
174 | &dai_props->slot_width); | ||
175 | if (ret) | ||
176 | return ret; | ||
177 | |||
178 | ret = asoc_simple_card_canonicalize_dailink(dai_link); | ||
179 | if (ret < 0) | ||
180 | return ret; | ||
181 | |||
182 | dai_link->dai_fmt = daifmt; | ||
183 | dai_link->dpcm_playback = 1; | ||
184 | dai_link->dpcm_capture = 1; | ||
185 | dai_link->ops = &asoc_graph_card_ops; | ||
186 | dai_link->init = asoc_graph_card_dai_init; | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static int asoc_graph_card_parse_of(struct graph_card_data *priv) | ||
192 | { | ||
193 | struct of_phandle_iterator it; | ||
194 | struct device *dev = graph_priv_to_dev(priv); | ||
195 | struct snd_soc_card *card = graph_priv_to_card(priv); | ||
196 | struct device_node *node = dev->of_node; | ||
197 | struct device_node *cpu_port; | ||
198 | struct device_node *cpu_ep; | ||
199 | struct device_node *codec_ep; | ||
200 | struct device_node *rcpu_ep; | ||
201 | unsigned int daifmt = 0; | ||
202 | int dai_idx, ret; | ||
203 | int rc, codec; | ||
204 | |||
205 | if (!node) | ||
206 | return -EINVAL; | ||
207 | |||
208 | /* | ||
209 | * we need to consider "widgets", "mclk-fs" around here | ||
210 | * see simple-card | ||
211 | */ | ||
212 | |||
213 | ret = snd_soc_of_parse_audio_routing(card, "routing"); | ||
214 | if (ret) | ||
215 | return ret; | ||
216 | |||
217 | /* sampling rate convert */ | ||
218 | of_property_read_u32(node, "convert-rate", &priv->convert_rate); | ||
219 | |||
220 | /* channels transfer */ | ||
221 | of_property_read_u32(node, "convert-channels", &priv->convert_channels); | ||
222 | |||
223 | /* | ||
224 | * it supports multi CPU, single CODEC only here | ||
225 | * see asoc_graph_get_dais_count | ||
226 | */ | ||
227 | |||
228 | /* find 1st codec */ | ||
229 | of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { | ||
230 | cpu_port = it.node; | ||
231 | cpu_ep = of_get_next_child(cpu_port, NULL); | ||
232 | codec_ep = of_graph_get_remote_endpoint(cpu_ep); | ||
233 | rcpu_ep = of_graph_get_remote_endpoint(codec_ep); | ||
234 | |||
235 | of_node_put(cpu_port); | ||
236 | of_node_put(cpu_ep); | ||
237 | of_node_put(codec_ep); | ||
238 | of_node_put(rcpu_ep); | ||
239 | |||
240 | if (!codec_ep) | ||
241 | continue; | ||
242 | |||
243 | if (rcpu_ep != cpu_ep) { | ||
244 | dev_err(dev, "remote-endpoint missmatch (%s/%s/%s)\n", | ||
245 | cpu_ep->name, codec_ep->name, rcpu_ep->name); | ||
246 | ret = -EINVAL; | ||
247 | goto parse_of_err; | ||
248 | } | ||
249 | |||
250 | ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, | ||
251 | NULL, &daifmt); | ||
252 | if (ret < 0) | ||
253 | goto parse_of_err; | ||
254 | } | ||
255 | |||
256 | dai_idx = 0; | ||
257 | for (codec = 0; codec < 2; codec++) { | ||
258 | /* | ||
259 | * To listup valid sounds continuously, | ||
260 | * detect all CPU-dummy first, and | ||
261 | * detect all dummy-Codec second | ||
262 | */ | ||
263 | of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { | ||
264 | cpu_port = it.node; | ||
265 | cpu_ep = of_get_next_child(cpu_port, NULL); | ||
266 | codec_ep = of_graph_get_remote_endpoint(cpu_ep); | ||
267 | |||
268 | of_node_put(cpu_port); | ||
269 | of_node_put(cpu_ep); | ||
270 | of_node_put(codec_ep); | ||
271 | |||
272 | if (codec) { | ||
273 | if (!codec_ep) | ||
274 | continue; | ||
275 | |||
276 | /* Back-End (= Codec) */ | ||
277 | ret = asoc_graph_card_dai_link_of(codec_ep, priv, daifmt, dai_idx++, 0); | ||
278 | if (ret < 0) | ||
279 | goto parse_of_err; | ||
280 | } else { | ||
281 | /* Front-End (= CPU) */ | ||
282 | ret = asoc_graph_card_dai_link_of(cpu_ep, priv, daifmt, dai_idx++, 1); | ||
283 | if (ret < 0) | ||
284 | goto parse_of_err; | ||
285 | } | ||
286 | } | ||
287 | } | ||
288 | |||
289 | ret = asoc_simple_card_parse_card_name(card, NULL); | ||
290 | if (ret) | ||
291 | goto parse_of_err; | ||
292 | |||
293 | dev_dbg(dev, "convert_rate %d\n", priv->convert_rate); | ||
294 | dev_dbg(dev, "convert_channels %d\n", priv->convert_channels); | ||
295 | |||
296 | ret = 0; | ||
297 | |||
298 | parse_of_err: | ||
299 | return ret; | ||
300 | } | ||
301 | |||
302 | static int asoc_graph_get_dais_count(struct device *dev) | ||
303 | { | ||
304 | struct of_phandle_iterator it; | ||
305 | struct device_node *node = dev->of_node; | ||
306 | struct device_node *cpu_port; | ||
307 | struct device_node *cpu_ep; | ||
308 | struct device_node *codec_ep; | ||
309 | int count = 0; | ||
310 | int rc; | ||
311 | |||
312 | of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { | ||
313 | cpu_port = it.node; | ||
314 | cpu_ep = of_get_next_child(cpu_port, NULL); | ||
315 | codec_ep = of_graph_get_remote_endpoint(cpu_ep); | ||
316 | |||
317 | of_node_put(cpu_port); | ||
318 | of_node_put(cpu_ep); | ||
319 | of_node_put(codec_ep); | ||
320 | |||
321 | if (cpu_ep) | ||
322 | count++; | ||
323 | if (codec_ep) | ||
324 | count++; | ||
325 | } | ||
326 | |||
327 | return count; | ||
328 | } | ||
329 | |||
330 | static int asoc_graph_card_probe(struct platform_device *pdev) | ||
331 | { | ||
332 | struct graph_card_data *priv; | ||
333 | struct snd_soc_dai_link *dai_link; | ||
334 | struct asoc_simple_dai *dai_props; | ||
335 | struct device *dev = &pdev->dev; | ||
336 | struct snd_soc_card *card; | ||
337 | int num, ret; | ||
338 | |||
339 | /* Allocate the private data and the DAI link array */ | ||
340 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
341 | if (!priv) | ||
342 | return -ENOMEM; | ||
343 | |||
344 | num = asoc_graph_get_dais_count(dev); | ||
345 | if (num == 0) | ||
346 | return -EINVAL; | ||
347 | |||
348 | dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL); | ||
349 | dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL); | ||
350 | if (!dai_props || !dai_link) | ||
351 | return -ENOMEM; | ||
352 | |||
353 | priv->dai_props = dai_props; | ||
354 | priv->dai_link = dai_link; | ||
355 | |||
356 | /* Init snd_soc_card */ | ||
357 | card = graph_priv_to_card(priv); | ||
358 | card->owner = THIS_MODULE; | ||
359 | card->dev = dev; | ||
360 | card->dai_link = priv->dai_link; | ||
361 | card->num_links = num; | ||
362 | card->codec_conf = &priv->codec_conf; | ||
363 | card->num_configs = 1; | ||
364 | |||
365 | ret = asoc_graph_card_parse_of(priv); | ||
366 | if (ret < 0) { | ||
367 | if (ret != -EPROBE_DEFER) | ||
368 | dev_err(dev, "parse error %d\n", ret); | ||
369 | goto err; | ||
370 | } | ||
371 | |||
372 | snd_soc_card_set_drvdata(card, priv); | ||
373 | |||
374 | ret = devm_snd_soc_register_card(dev, card); | ||
375 | if (ret < 0) | ||
376 | goto err; | ||
377 | |||
378 | return 0; | ||
379 | err: | ||
380 | asoc_simple_card_clean_reference(card); | ||
381 | |||
382 | return ret; | ||
383 | } | ||
384 | |||
385 | static int asoc_graph_card_remove(struct platform_device *pdev) | ||
386 | { | ||
387 | struct snd_soc_card *card = platform_get_drvdata(pdev); | ||
388 | |||
389 | return asoc_simple_card_clean_reference(card); | ||
390 | } | ||
391 | |||
392 | static const struct of_device_id asoc_graph_of_match[] = { | ||
393 | { .compatible = "audio-graph-scu-card", }, | ||
394 | {}, | ||
395 | }; | ||
396 | MODULE_DEVICE_TABLE(of, asoc_graph_of_match); | ||
397 | |||
398 | static struct platform_driver asoc_graph_card = { | ||
399 | .driver = { | ||
400 | .name = "asoc-audio-graph-scu-card", | ||
401 | .of_match_table = asoc_graph_of_match, | ||
402 | }, | ||
403 | .probe = asoc_graph_card_probe, | ||
404 | .remove = asoc_graph_card_remove, | ||
405 | }; | ||
406 | module_platform_driver(asoc_graph_card); | ||
407 | |||
408 | MODULE_ALIAS("platform:asoc-audio-graph-scu-card"); | ||
409 | MODULE_LICENSE("GPL v2"); | ||
410 | MODULE_DESCRIPTION("ASoC Audio Graph SCU Sound Card"); | ||
411 | MODULE_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..d9d8b8a58348 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 | ||
15 | int asoc_simple_card_parse_daifmt(struct device *dev, | 16 | int asoc_simple_card_parse_daifmt(struct device *dev, |
@@ -20,14 +21,13 @@ int asoc_simple_card_parse_daifmt(struct device *dev, | |||
20 | { | 21 | { |
21 | struct device_node *bitclkmaster = NULL; | 22 | struct device_node *bitclkmaster = NULL; |
22 | struct device_node *framemaster = NULL; | 23 | struct device_node *framemaster = NULL; |
23 | int prefix_len = prefix ? strlen(prefix) : 0; | ||
24 | unsigned int daifmt; | 24 | unsigned int daifmt; |
25 | 25 | ||
26 | daifmt = snd_soc_of_parse_daifmt(node, prefix, | 26 | daifmt = snd_soc_of_parse_daifmt(node, prefix, |
27 | &bitclkmaster, &framemaster); | 27 | &bitclkmaster, &framemaster); |
28 | daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; | 28 | daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; |
29 | 29 | ||
30 | if (prefix_len && !bitclkmaster && !framemaster) { | 30 | if (!bitclkmaster && !framemaster) { |
31 | /* | 31 | /* |
32 | * No dai-link level and master setting was not found from | 32 | * No dai-link level and master setting was not found from |
33 | * sound node level, revert back to legacy DT parsing and | 33 | * sound node level, revert back to legacy DT parsing and |
@@ -51,6 +51,8 @@ int asoc_simple_card_parse_daifmt(struct device *dev, | |||
51 | 51 | ||
52 | *retfmt = daifmt; | 52 | *retfmt = daifmt; |
53 | 53 | ||
54 | dev_dbg(dev, "format : %04x\n", daifmt); | ||
55 | |||
54 | return 0; | 56 | return 0; |
55 | } | 57 | } |
56 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt); | 58 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt); |
@@ -72,6 +74,8 @@ int asoc_simple_card_set_dailink_name(struct device *dev, | |||
72 | 74 | ||
73 | dai_link->name = name; | 75 | dai_link->name = name; |
74 | dai_link->stream_name = name; | 76 | dai_link->stream_name = name; |
77 | |||
78 | dev_dbg(dev, "name : %s\n", name); | ||
75 | } | 79 | } |
76 | 80 | ||
77 | return ret; | 81 | return ret; |
@@ -81,19 +85,27 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name); | |||
81 | int asoc_simple_card_parse_card_name(struct snd_soc_card *card, | 85 | int asoc_simple_card_parse_card_name(struct snd_soc_card *card, |
82 | char *prefix) | 86 | char *prefix) |
83 | { | 87 | { |
84 | char prop[128]; | ||
85 | int ret; | 88 | int ret; |
86 | 89 | ||
87 | snprintf(prop, sizeof(prop), "%sname", prefix); | 90 | if (!prefix) |
91 | prefix = ""; | ||
88 | 92 | ||
89 | /* Parse the card name from DT */ | 93 | /* Parse the card name from DT */ |
90 | ret = snd_soc_of_parse_card_name(card, prop); | 94 | ret = snd_soc_of_parse_card_name(card, "label"); |
91 | if (ret < 0) | 95 | if (ret < 0) { |
92 | return ret; | 96 | char prop[128]; |
97 | |||
98 | snprintf(prop, sizeof(prop), "%sname", prefix); | ||
99 | ret = snd_soc_of_parse_card_name(card, prop); | ||
100 | if (ret < 0) | ||
101 | return ret; | ||
102 | } | ||
93 | 103 | ||
94 | if (!card->name && card->dai_link) | 104 | if (!card->name && card->dai_link) |
95 | card->name = card->dai_link->name; | 105 | card->name = card->dai_link->name; |
96 | 106 | ||
107 | dev_dbg(card->dev, "Card Name: %s\n", card->name ? card->name : ""); | ||
108 | |||
97 | return 0; | 109 | return 0; |
98 | } | 110 | } |
99 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name); | 111 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name); |
@@ -101,7 +113,8 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name); | |||
101 | int asoc_simple_card_parse_clk(struct device *dev, | 113 | int asoc_simple_card_parse_clk(struct device *dev, |
102 | struct device_node *node, | 114 | struct device_node *node, |
103 | struct device_node *dai_of_node, | 115 | struct device_node *dai_of_node, |
104 | struct asoc_simple_dai *simple_dai) | 116 | struct asoc_simple_dai *simple_dai, |
117 | const char *name) | ||
105 | { | 118 | { |
106 | struct clk *clk; | 119 | struct clk *clk; |
107 | u32 val; | 120 | u32 val; |
@@ -124,6 +137,8 @@ int asoc_simple_card_parse_clk(struct device *dev, | |||
124 | simple_dai->sysclk = clk_get_rate(clk); | 137 | simple_dai->sysclk = clk_get_rate(clk); |
125 | } | 138 | } |
126 | 139 | ||
140 | dev_dbg(dev, "%s : sysclk = %d\n", name, simple_dai->sysclk); | ||
141 | |||
127 | return 0; | 142 | return 0; |
128 | } | 143 | } |
129 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk); | 144 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk); |
@@ -165,6 +180,71 @@ int asoc_simple_card_parse_dai(struct device_node *node, | |||
165 | } | 180 | } |
166 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai); | 181 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai); |
167 | 182 | ||
183 | static int asoc_simple_card_get_dai_id(struct device_node *ep) | ||
184 | { | ||
185 | struct device_node *node; | ||
186 | struct device_node *endpoint; | ||
187 | int i, id; | ||
188 | int ret; | ||
189 | |||
190 | ret = snd_soc_get_dai_id(ep); | ||
191 | if (ret != -ENOTSUPP) | ||
192 | return ret; | ||
193 | |||
194 | node = of_graph_get_port_parent(ep); | ||
195 | |||
196 | /* | ||
197 | * Non HDMI sound case, counting port/endpoint on its DT | ||
198 | * is enough. Let's count it. | ||
199 | */ | ||
200 | i = 0; | ||
201 | id = -1; | ||
202 | for_each_endpoint_of_node(node, endpoint) { | ||
203 | if (endpoint == ep) | ||
204 | id = i; | ||
205 | i++; | ||
206 | } | ||
207 | if (id < 0) | ||
208 | return -ENODEV; | ||
209 | |||
210 | return id; | ||
211 | } | ||
212 | |||
213 | int asoc_simple_card_parse_graph_dai(struct device_node *ep, | ||
214 | struct device_node **dai_of_node, | ||
215 | const char **dai_name) | ||
216 | { | ||
217 | struct device_node *node; | ||
218 | struct of_phandle_args args; | ||
219 | int ret; | ||
220 | |||
221 | if (!ep) | ||
222 | return 0; | ||
223 | if (!dai_name) | ||
224 | return 0; | ||
225 | |||
226 | /* | ||
227 | * of_graph_get_port_parent() will call | ||
228 | * of_node_put(). So, call of_node_get() here | ||
229 | */ | ||
230 | of_node_get(ep); | ||
231 | node = of_graph_get_port_parent(ep); | ||
232 | |||
233 | /* Get dai->name */ | ||
234 | args.np = node; | ||
235 | args.args[0] = asoc_simple_card_get_dai_id(ep); | ||
236 | args.args_count = (of_graph_get_endpoint_count(node) > 1); | ||
237 | |||
238 | ret = snd_soc_get_dai_name(&args, dai_name); | ||
239 | if (ret < 0) | ||
240 | return ret; | ||
241 | |||
242 | *dai_of_node = node; | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | EXPORT_SYMBOL_GPL(asoc_simple_card_parse_graph_dai); | ||
247 | |||
168 | int asoc_simple_card_init_dai(struct snd_soc_dai *dai, | 248 | int asoc_simple_card_init_dai(struct snd_soc_dai *dai, |
169 | struct asoc_simple_dai *simple_dai) | 249 | struct asoc_simple_dai *simple_dai) |
170 | { | 250 | { |
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 2c9dedab5184..e86c6e16146b 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c | |||
@@ -301,15 +301,6 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, | |||
301 | dai_link->ops = &asoc_simple_card_ops; | 301 | dai_link->ops = &asoc_simple_card_ops; |
302 | dai_link->init = asoc_simple_card_dai_init; | 302 | dai_link->init = asoc_simple_card_dai_init; |
303 | 303 | ||
304 | dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); | ||
305 | dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); | ||
306 | dev_dbg(dev, "\tcpu : %s / %d\n", | ||
307 | dai_link->cpu_dai_name, | ||
308 | dai_props->cpu_dai.sysclk); | ||
309 | dev_dbg(dev, "\tcodec : %s / %d\n", | ||
310 | dai_link->codec_dai_name, | ||
311 | dai_props->codec_dai.sysclk); | ||
312 | |||
313 | asoc_simple_card_canonicalize_cpu(dai_link, single_cpu); | 304 | asoc_simple_card_canonicalize_cpu(dai_link, single_cpu); |
314 | 305 | ||
315 | dai_link_of_err: | 306 | dai_link_of_err: |
@@ -497,8 +488,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev) | |||
497 | snd_soc_card_set_drvdata(card, priv); | 488 | snd_soc_card_set_drvdata(card, priv); |
498 | 489 | ||
499 | ret = devm_snd_soc_register_card(dev, card); | 490 | ret = devm_snd_soc_register_card(dev, card); |
500 | if (ret >= 0) | 491 | if (ret < 0) |
501 | return ret; | 492 | goto err; |
493 | |||
494 | return 0; | ||
502 | err: | 495 | err: |
503 | asoc_simple_card_clean_reference(card); | 496 | asoc_simple_card_clean_reference(card); |
504 | 497 | ||
diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index dcbcab230d1b..5faf5d6c48a2 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c | |||
@@ -189,21 +189,16 @@ static int asoc_simple_card_dai_link_of(struct device_node *np, | |||
189 | dai_link->ops = &asoc_simple_card_ops; | 189 | dai_link->ops = &asoc_simple_card_ops; |
190 | dai_link->init = asoc_simple_card_dai_init; | 190 | dai_link->init = asoc_simple_card_dai_init; |
191 | 191 | ||
192 | dev_dbg(dev, "\t%s / %04x / %d\n", | ||
193 | dai_link->name, | ||
194 | dai_link->dai_fmt, | ||
195 | dai_props->sysclk); | ||
196 | |||
197 | return 0; | 192 | return 0; |
198 | } | 193 | } |
199 | 194 | ||
200 | static int asoc_simple_card_parse_of(struct device_node *node, | 195 | static int asoc_simple_card_parse_of(struct simple_card_data *priv) |
201 | struct simple_card_data *priv) | ||
202 | 196 | ||
203 | { | 197 | { |
204 | struct device *dev = simple_priv_to_dev(priv); | 198 | struct device *dev = simple_priv_to_dev(priv); |
205 | struct device_node *np; | 199 | struct device_node *np; |
206 | struct snd_soc_card *card = simple_priv_to_card(priv); | 200 | struct snd_soc_card *card = simple_priv_to_card(priv); |
201 | struct device_node *node = dev->of_node; | ||
207 | unsigned int daifmt = 0; | 202 | unsigned int daifmt = 0; |
208 | bool is_fe; | 203 | bool is_fe; |
209 | int ret, i; | 204 | int ret, i; |
@@ -246,8 +241,6 @@ static int asoc_simple_card_parse_of(struct device_node *node, | |||
246 | if (ret < 0) | 241 | if (ret < 0) |
247 | return ret; | 242 | return ret; |
248 | 243 | ||
249 | dev_dbg(dev, "New card: %s\n", | ||
250 | card->name ? card->name : ""); | ||
251 | dev_dbg(dev, "convert_rate %d\n", priv->convert_rate); | 244 | dev_dbg(dev, "convert_rate %d\n", priv->convert_rate); |
252 | dev_dbg(dev, "convert_channels %d\n", priv->convert_channels); | 245 | dev_dbg(dev, "convert_channels %d\n", priv->convert_channels); |
253 | 246 | ||
@@ -288,7 +281,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) | |||
288 | card->codec_conf = &priv->codec_conf; | 281 | card->codec_conf = &priv->codec_conf; |
289 | card->num_configs = 1; | 282 | card->num_configs = 1; |
290 | 283 | ||
291 | ret = asoc_simple_card_parse_of(np, priv); | 284 | ret = asoc_simple_card_parse_of(priv); |
292 | if (ret < 0) { | 285 | if (ret < 0) { |
293 | if (ret != -EPROBE_DEFER) | 286 | if (ret != -EPROBE_DEFER) |
294 | dev_err(dev, "parse error %d\n", ret); | 287 | dev_err(dev, "parse error %d\n", ret); |
@@ -298,8 +291,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev) | |||
298 | snd_soc_card_set_drvdata(card, priv); | 291 | snd_soc_card_set_drvdata(card, priv); |
299 | 292 | ||
300 | ret = devm_snd_soc_register_card(dev, card); | 293 | ret = devm_snd_soc_register_card(dev, card); |
301 | if (ret >= 0) | 294 | if (ret < 0) |
302 | return ret; | 295 | goto err; |
296 | |||
297 | return 0; | ||
303 | err: | 298 | err: |
304 | asoc_simple_card_clean_reference(card); | 299 | asoc_simple_card_clean_reference(card); |
305 | 300 | ||
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 147ebecfed94..1aa5cd77ca24 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig | |||
@@ -38,7 +38,7 @@ config SND_SOC_RCAR | |||
38 | tristate "R-Car series SRU/SCU/SSIU/SSI support" | 38 | tristate "R-Car series SRU/SCU/SSIU/SSI support" |
39 | depends on COMMON_CLK | 39 | depends on COMMON_CLK |
40 | depends on OF || COMPILE_TEST | 40 | depends on OF || COMPILE_TEST |
41 | select SND_SIMPLE_CARD | 41 | select SND_SIMPLE_CARD_UTILS |
42 | select REGMAP_MMIO | 42 | select REGMAP_MMIO |
43 | help | 43 | help |
44 | This option enables R-Car SRU/SCU/SSIU/SSI sound support | 44 | This option enables R-Car SRU/SCU/SSIU/SSI sound support |
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index d3b0dc145a56..4a72fd74ddc2 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c | |||
@@ -480,6 +480,9 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, | |||
480 | if (req_rate[0] % 48000 == 0) | 480 | if (req_rate[0] % 48000 == 0) |
481 | adg->flags = AUDIO_OUT_48; | 481 | adg->flags = AUDIO_OUT_48; |
482 | 482 | ||
483 | if (of_get_property(np, "clkout-lr-asynchronous", NULL)) | ||
484 | adg->flags = LRCLK_ASYNC; | ||
485 | |||
483 | /* | 486 | /* |
484 | * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC | 487 | * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC |
485 | * have 44.1kHz or 48kHz base clocks for now. | 488 | * have 44.1kHz or 48kHz base clocks for now. |
@@ -555,7 +558,6 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv, | |||
555 | clk = clk_register_fixed_rate(dev, clkout_name[i], | 558 | clk = clk_register_fixed_rate(dev, clkout_name[i], |
556 | parent_clk_name, 0, | 559 | parent_clk_name, 0, |
557 | req_rate[0]); | 560 | req_rate[0]); |
558 | adg->clkout[i] = ERR_PTR(-ENOENT); | ||
559 | if (!IS_ERR(clk)) | 561 | if (!IS_ERR(clk)) |
560 | adg->clkout[i] = clk; | 562 | adg->clkout[i] = clk; |
561 | } | 563 | } |
@@ -580,7 +582,6 @@ int rsnd_adg_probe(struct rsnd_priv *priv) | |||
580 | { | 582 | { |
581 | struct rsnd_adg *adg; | 583 | struct rsnd_adg *adg; |
582 | struct device *dev = rsnd_priv_to_dev(priv); | 584 | struct device *dev = rsnd_priv_to_dev(priv); |
583 | struct device_node *np = dev->of_node; | ||
584 | int ret; | 585 | int ret; |
585 | 586 | ||
586 | adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); | 587 | adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); |
@@ -597,9 +598,6 @@ int rsnd_adg_probe(struct rsnd_priv *priv) | |||
597 | rsnd_adg_get_clkin(priv, adg); | 598 | rsnd_adg_get_clkin(priv, adg); |
598 | rsnd_adg_get_clkout(priv, adg); | 599 | rsnd_adg_get_clkout(priv, adg); |
599 | 600 | ||
600 | if (of_get_property(np, "clkout-lr-asynchronous", NULL)) | ||
601 | adg->flags = LRCLK_ASYNC; | ||
602 | |||
603 | priv->adg = adg; | 601 | priv->adg = adg; |
604 | 602 | ||
605 | rsnd_adg_clk_enable(priv); | 603 | rsnd_adg_clk_enable(priv); |
diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index d879c010cf03..9a136d86e2a9 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c | |||
@@ -82,6 +82,9 @@ static int rsnd_cmd_init(struct rsnd_mod *mod, | |||
82 | [9] = 0x2, | 82 | [9] = 0x2, |
83 | }; | 83 | }; |
84 | 84 | ||
85 | if (unlikely(!src)) | ||
86 | return -EIO; | ||
87 | |||
85 | data = path[rsnd_mod_id(src)] | | 88 | data = path[rsnd_mod_id(src)] | |
86 | cmd_case[rsnd_mod_id(src)] << 16; | 89 | cmd_case[rsnd_mod_id(src)] << 16; |
87 | } | 90 | } |
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 8c1f4e2e0c4f..7a8c08933503 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c | |||
@@ -766,11 +766,16 @@ static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream, | |||
766 | { | 766 | { |
767 | struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); | 767 | struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); |
768 | struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); | 768 | struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); |
769 | int ret; | ||
769 | 770 | ||
770 | /* | 771 | /* |
771 | * call rsnd_dai_call without spinlock | 772 | * call rsnd_dai_call without spinlock |
772 | */ | 773 | */ |
773 | return rsnd_dai_call(nolock_start, io, priv); | 774 | ret = rsnd_dai_call(nolock_start, io, priv); |
775 | if (ret < 0) | ||
776 | rsnd_dai_call(nolock_stop, io, priv); | ||
777 | |||
778 | return ret; | ||
774 | } | 779 | } |
775 | 780 | ||
776 | static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream, | 781 | static void rsnd_soc_dai_shutdown(struct snd_pcm_substream *substream, |
@@ -820,32 +825,131 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, | |||
820 | of_node_put(node); | 825 | of_node_put(node); |
821 | } | 826 | } |
822 | 827 | ||
823 | static int rsnd_dai_probe(struct rsnd_priv *priv) | 828 | static struct device_node *rsnd_dai_of_node(struct rsnd_priv *priv, |
829 | int *is_graph) | ||
824 | { | 830 | { |
831 | struct device *dev = rsnd_priv_to_dev(priv); | ||
832 | struct device_node *np = dev->of_node; | ||
825 | struct device_node *dai_node; | 833 | struct device_node *dai_node; |
826 | struct device_node *dai_np; | 834 | struct device_node *ret; |
835 | |||
836 | *is_graph = 0; | ||
837 | |||
838 | /* | ||
839 | * parse both previous dai (= rcar_sound,dai), and | ||
840 | * graph dai (= ports/port) | ||
841 | */ | ||
842 | dai_node = of_get_child_by_name(np, RSND_NODE_DAI); | ||
843 | if (dai_node) { | ||
844 | ret = dai_node; | ||
845 | goto of_node_compatible; | ||
846 | } | ||
847 | |||
848 | ret = np; | ||
849 | |||
850 | dai_node = of_graph_get_next_endpoint(np, NULL); | ||
851 | if (dai_node) | ||
852 | goto of_node_graph; | ||
853 | |||
854 | return NULL; | ||
855 | |||
856 | of_node_graph: | ||
857 | *is_graph = 1; | ||
858 | of_node_compatible: | ||
859 | of_node_put(dai_node); | ||
860 | |||
861 | return ret; | ||
862 | } | ||
863 | |||
864 | static void __rsnd_dai_probe(struct rsnd_priv *priv, | ||
865 | struct device_node *dai_np, | ||
866 | int dai_i, int is_graph) | ||
867 | { | ||
827 | struct device_node *playback, *capture; | 868 | struct device_node *playback, *capture; |
828 | struct rsnd_dai_stream *io_playback; | 869 | struct rsnd_dai_stream *io_playback; |
829 | struct rsnd_dai_stream *io_capture; | 870 | struct rsnd_dai_stream *io_capture; |
830 | struct snd_soc_dai_driver *rdrv, *drv; | 871 | struct snd_soc_dai_driver *drv; |
831 | struct rsnd_dai *rdai; | 872 | struct rsnd_dai *rdai; |
832 | struct device *dev = rsnd_priv_to_dev(priv); | 873 | struct device *dev = rsnd_priv_to_dev(priv); |
833 | int nr, dai_i, io_i; | 874 | int io_i; |
834 | int ret; | 875 | |
876 | rdai = rsnd_rdai_get(priv, dai_i); | ||
877 | drv = priv->daidrv + dai_i; | ||
878 | io_playback = &rdai->playback; | ||
879 | io_capture = &rdai->capture; | ||
880 | |||
881 | snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i); | ||
882 | |||
883 | rdai->priv = priv; | ||
884 | drv->name = rdai->name; | ||
885 | drv->ops = &rsnd_soc_dai_ops; | ||
886 | |||
887 | snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE, | ||
888 | "DAI%d Playback", dai_i); | ||
889 | drv->playback.rates = RSND_RATES; | ||
890 | drv->playback.formats = RSND_FMTS; | ||
891 | drv->playback.channels_min = 2; | ||
892 | drv->playback.channels_max = 6; | ||
893 | drv->playback.stream_name = rdai->playback.name; | ||
894 | |||
895 | snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE, | ||
896 | "DAI%d Capture", dai_i); | ||
897 | drv->capture.rates = RSND_RATES; | ||
898 | drv->capture.formats = RSND_FMTS; | ||
899 | drv->capture.channels_min = 2; | ||
900 | drv->capture.channels_max = 6; | ||
901 | drv->capture.stream_name = rdai->capture.name; | ||
902 | |||
903 | rdai->playback.rdai = rdai; | ||
904 | rdai->capture.rdai = rdai; | ||
905 | rsnd_set_slot(rdai, 2, 1); /* default */ | ||
906 | |||
907 | for (io_i = 0;; io_i++) { | ||
908 | playback = of_parse_phandle(dai_np, "playback", io_i); | ||
909 | capture = of_parse_phandle(dai_np, "capture", io_i); | ||
910 | |||
911 | if (!playback && !capture) | ||
912 | break; | ||
835 | 913 | ||
836 | dai_node = rsnd_dai_of_node(priv); | 914 | rsnd_parse_connect_ssi(rdai, playback, capture); |
837 | nr = of_get_child_count(dai_node); | 915 | rsnd_parse_connect_src(rdai, playback, capture); |
838 | if (!nr) { | 916 | rsnd_parse_connect_ctu(rdai, playback, capture); |
839 | ret = -EINVAL; | 917 | rsnd_parse_connect_mix(rdai, playback, capture); |
840 | goto rsnd_dai_probe_done; | 918 | rsnd_parse_connect_dvc(rdai, playback, capture); |
919 | |||
920 | of_node_put(playback); | ||
921 | of_node_put(capture); | ||
841 | } | 922 | } |
842 | 923 | ||
924 | dev_dbg(dev, "%s (%s/%s)\n", rdai->name, | ||
925 | rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ", | ||
926 | rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- "); | ||
927 | } | ||
928 | |||
929 | static int rsnd_dai_probe(struct rsnd_priv *priv) | ||
930 | { | ||
931 | struct device_node *dai_node; | ||
932 | struct device_node *dai_np; | ||
933 | struct snd_soc_dai_driver *rdrv; | ||
934 | struct device *dev = rsnd_priv_to_dev(priv); | ||
935 | struct rsnd_dai *rdai; | ||
936 | int nr; | ||
937 | int is_graph; | ||
938 | int dai_i; | ||
939 | |||
940 | dai_node = rsnd_dai_of_node(priv, &is_graph); | ||
941 | if (is_graph) | ||
942 | nr = of_graph_get_endpoint_count(dai_node); | ||
943 | else | ||
944 | nr = of_get_child_count(dai_node); | ||
945 | |||
946 | if (!nr) | ||
947 | return -EINVAL; | ||
948 | |||
843 | rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL); | 949 | rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL); |
844 | rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL); | 950 | rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL); |
845 | if (!rdrv || !rdai) { | 951 | if (!rdrv || !rdai) |
846 | ret = -ENOMEM; | 952 | return -ENOMEM; |
847 | goto rsnd_dai_probe_done; | ||
848 | } | ||
849 | 953 | ||
850 | priv->rdai_nr = nr; | 954 | priv->rdai_nr = nr; |
851 | priv->daidrv = rdrv; | 955 | priv->daidrv = rdrv; |
@@ -855,68 +959,18 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) | |||
855 | * parse all dai | 959 | * parse all dai |
856 | */ | 960 | */ |
857 | dai_i = 0; | 961 | dai_i = 0; |
858 | for_each_child_of_node(dai_node, dai_np) { | 962 | if (is_graph) { |
859 | rdai = rsnd_rdai_get(priv, dai_i); | 963 | for_each_endpoint_of_node(dai_node, dai_np) { |
860 | drv = rdrv + dai_i; | 964 | __rsnd_dai_probe(priv, dai_np, dai_i, is_graph); |
861 | io_playback = &rdai->playback; | 965 | rsnd_ssi_parse_hdmi_connection(priv, dai_np, dai_i); |
862 | io_capture = &rdai->capture; | 966 | dai_i++; |
863 | |||
864 | snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i); | ||
865 | |||
866 | rdai->priv = priv; | ||
867 | drv->name = rdai->name; | ||
868 | drv->ops = &rsnd_soc_dai_ops; | ||
869 | |||
870 | snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE, | ||
871 | "DAI%d Playback", dai_i); | ||
872 | drv->playback.rates = RSND_RATES; | ||
873 | drv->playback.formats = RSND_FMTS; | ||
874 | drv->playback.channels_min = 2; | ||
875 | drv->playback.channels_max = 6; | ||
876 | drv->playback.stream_name = rdai->playback.name; | ||
877 | |||
878 | snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE, | ||
879 | "DAI%d Capture", dai_i); | ||
880 | drv->capture.rates = RSND_RATES; | ||
881 | drv->capture.formats = RSND_FMTS; | ||
882 | drv->capture.channels_min = 2; | ||
883 | drv->capture.channels_max = 6; | ||
884 | drv->capture.stream_name = rdai->capture.name; | ||
885 | |||
886 | rdai->playback.rdai = rdai; | ||
887 | rdai->capture.rdai = rdai; | ||
888 | rsnd_set_slot(rdai, 2, 1); /* default */ | ||
889 | |||
890 | for (io_i = 0;; io_i++) { | ||
891 | playback = of_parse_phandle(dai_np, "playback", io_i); | ||
892 | capture = of_parse_phandle(dai_np, "capture", io_i); | ||
893 | |||
894 | if (!playback && !capture) | ||
895 | break; | ||
896 | |||
897 | rsnd_parse_connect_ssi(rdai, playback, capture); | ||
898 | rsnd_parse_connect_src(rdai, playback, capture); | ||
899 | rsnd_parse_connect_ctu(rdai, playback, capture); | ||
900 | rsnd_parse_connect_mix(rdai, playback, capture); | ||
901 | rsnd_parse_connect_dvc(rdai, playback, capture); | ||
902 | |||
903 | of_node_put(playback); | ||
904 | of_node_put(capture); | ||
905 | } | 967 | } |
906 | 968 | } else { | |
907 | dai_i++; | 969 | for_each_child_of_node(dai_node, dai_np) |
908 | 970 | __rsnd_dai_probe(priv, dai_np, dai_i++, is_graph); | |
909 | dev_dbg(dev, "%s (%s/%s)\n", rdai->name, | ||
910 | rsnd_io_to_mod_ssi(io_playback) ? "play" : " -- ", | ||
911 | rsnd_io_to_mod_ssi(io_capture) ? "capture" : " -- "); | ||
912 | } | 971 | } |
913 | 972 | ||
914 | ret = 0; | 973 | return 0; |
915 | |||
916 | rsnd_dai_probe_done: | ||
917 | of_node_put(dai_node); | ||
918 | |||
919 | return ret; | ||
920 | } | 974 | } |
921 | 975 | ||
922 | /* | 976 | /* |
@@ -965,12 +1019,14 @@ static int rsnd_hw_params(struct snd_pcm_substream *substream, | |||
965 | 1019 | ||
966 | static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream) | 1020 | static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream) |
967 | { | 1021 | { |
968 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
969 | struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); | 1022 | struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); |
970 | struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); | 1023 | struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); |
971 | struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); | 1024 | struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); |
1025 | snd_pcm_uframes_t pointer = 0; | ||
1026 | |||
1027 | rsnd_dai_call(pointer, io, &pointer); | ||
972 | 1028 | ||
973 | return bytes_to_frames(runtime, io->byte_pos); | 1029 | return pointer; |
974 | } | 1030 | } |
975 | 1031 | ||
976 | static struct snd_pcm_ops rsnd_pcm_ops = { | 1032 | static struct snd_pcm_ops rsnd_pcm_ops = { |
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 241cb3b08a07..05e538f4c8d5 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c | |||
@@ -25,6 +25,7 @@ | |||
25 | 25 | ||
26 | struct rsnd_dmaen { | 26 | struct rsnd_dmaen { |
27 | struct dma_chan *chan; | 27 | struct dma_chan *chan; |
28 | dma_cookie_t cookie; | ||
28 | dma_addr_t dma_buf; | 29 | dma_addr_t dma_buf; |
29 | unsigned int dma_len; | 30 | unsigned int dma_len; |
30 | unsigned int dma_period; | 31 | unsigned int dma_period; |
@@ -292,7 +293,8 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, | |||
292 | for (i = 0; i < 2; i++) | 293 | for (i = 0; i < 2; i++) |
293 | rsnd_dmaen_sync(dmaen, io, i); | 294 | rsnd_dmaen_sync(dmaen, io, i); |
294 | 295 | ||
295 | if (dmaengine_submit(desc) < 0) { | 296 | dmaen->cookie = dmaengine_submit(desc); |
297 | if (dmaen->cookie < 0) { | ||
296 | dev_err(dev, "dmaengine_submit() fail\n"); | 298 | dev_err(dev, "dmaengine_submit() fail\n"); |
297 | return -EIO; | 299 | return -EIO; |
298 | } | 300 | } |
@@ -348,12 +350,34 @@ static int rsnd_dmaen_attach(struct rsnd_dai_stream *io, | |||
348 | return 0; | 350 | return 0; |
349 | } | 351 | } |
350 | 352 | ||
353 | static int rsnd_dmaen_pointer(struct rsnd_mod *mod, | ||
354 | struct rsnd_dai_stream *io, | ||
355 | snd_pcm_uframes_t *pointer) | ||
356 | { | ||
357 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | ||
358 | struct rsnd_dma *dma = rsnd_mod_to_dma(mod); | ||
359 | struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); | ||
360 | struct dma_tx_state state; | ||
361 | enum dma_status status; | ||
362 | unsigned int pos = 0; | ||
363 | |||
364 | status = dmaengine_tx_status(dmaen->chan, dmaen->cookie, &state); | ||
365 | if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { | ||
366 | if (state.residue > 0 && state.residue <= dmaen->dma_len) | ||
367 | pos = dmaen->dma_len - state.residue; | ||
368 | } | ||
369 | *pointer = bytes_to_frames(runtime, pos); | ||
370 | |||
371 | return 0; | ||
372 | } | ||
373 | |||
351 | static struct rsnd_mod_ops rsnd_dmaen_ops = { | 374 | static struct rsnd_mod_ops rsnd_dmaen_ops = { |
352 | .name = "audmac", | 375 | .name = "audmac", |
353 | .nolock_start = rsnd_dmaen_nolock_start, | 376 | .nolock_start = rsnd_dmaen_nolock_start, |
354 | .nolock_stop = rsnd_dmaen_nolock_stop, | 377 | .nolock_stop = rsnd_dmaen_nolock_stop, |
355 | .start = rsnd_dmaen_start, | 378 | .start = rsnd_dmaen_start, |
356 | .stop = rsnd_dmaen_stop, | 379 | .stop = rsnd_dmaen_stop, |
380 | .pointer= rsnd_dmaen_pointer, | ||
357 | }; | 381 | }; |
358 | 382 | ||
359 | /* | 383 | /* |
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 4b0980728e13..ee00e3516911 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c | |||
@@ -219,6 +219,8 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv) | |||
219 | RSND_GEN_S_REG(SSI_SYS_STATUS5, 0x884), | 219 | RSND_GEN_S_REG(SSI_SYS_STATUS5, 0x884), |
220 | RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888), | 220 | RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888), |
221 | RSND_GEN_S_REG(SSI_SYS_STATUS7, 0x88c), | 221 | RSND_GEN_S_REG(SSI_SYS_STATUS7, 0x88c), |
222 | RSND_GEN_S_REG(HDMI0_SEL, 0x9e0), | ||
223 | RSND_GEN_S_REG(HDMI1_SEL, 0x9e4), | ||
222 | 224 | ||
223 | /* FIXME: it needs SSI_MODE2/3 in the future */ | 225 | /* FIXME: it needs SSI_MODE2/3 in the future */ |
224 | RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80), | 226 | RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80), |
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 323af41ecfcb..bd8def0bc212 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/list.h> | 18 | #include <linux/list.h> |
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/of_device.h> | 20 | #include <linux/of_device.h> |
21 | #include <linux/of_graph.h> | ||
21 | #include <linux/of_irq.h> | 22 | #include <linux/of_irq.h> |
22 | #include <linux/sh_dma.h> | 23 | #include <linux/sh_dma.h> |
23 | #include <linux/workqueue.h> | 24 | #include <linux/workqueue.h> |
@@ -170,6 +171,8 @@ enum rsnd_reg { | |||
170 | RSND_REG_SSI_SYS_STATUS5, | 171 | RSND_REG_SSI_SYS_STATUS5, |
171 | RSND_REG_SSI_SYS_STATUS6, | 172 | RSND_REG_SSI_SYS_STATUS6, |
172 | RSND_REG_SSI_SYS_STATUS7, | 173 | RSND_REG_SSI_SYS_STATUS7, |
174 | RSND_REG_HDMI0_SEL, | ||
175 | RSND_REG_HDMI1_SEL, | ||
173 | 176 | ||
174 | /* SSI */ | 177 | /* SSI */ |
175 | RSND_REG_SSICR, | 178 | RSND_REG_SSICR, |
@@ -268,6 +271,9 @@ struct rsnd_mod_ops { | |||
268 | struct rsnd_dai_stream *io, | 271 | struct rsnd_dai_stream *io, |
269 | struct snd_pcm_substream *substream, | 272 | struct snd_pcm_substream *substream, |
270 | struct snd_pcm_hw_params *hw_params); | 273 | struct snd_pcm_hw_params *hw_params); |
274 | int (*pointer)(struct rsnd_mod *mod, | ||
275 | struct rsnd_dai_stream *io, | ||
276 | snd_pcm_uframes_t *pointer); | ||
271 | int (*fallback)(struct rsnd_mod *mod, | 277 | int (*fallback)(struct rsnd_mod *mod, |
272 | struct rsnd_dai_stream *io, | 278 | struct rsnd_dai_stream *io, |
273 | struct rsnd_priv *priv); | 279 | struct rsnd_priv *priv); |
@@ -305,6 +311,7 @@ struct rsnd_mod { | |||
305 | * H 0: pcm_new | 311 | * H 0: pcm_new |
306 | * H 0: fallback | 312 | * H 0: fallback |
307 | * H 0: hw_params | 313 | * H 0: hw_params |
314 | * H 0: pointer | ||
308 | */ | 315 | */ |
309 | #define __rsnd_mod_shift_nolock_start 0 | 316 | #define __rsnd_mod_shift_nolock_start 0 |
310 | #define __rsnd_mod_shift_nolock_stop 0 | 317 | #define __rsnd_mod_shift_nolock_stop 0 |
@@ -318,6 +325,7 @@ struct rsnd_mod { | |||
318 | #define __rsnd_mod_shift_pcm_new 28 /* always called */ | 325 | #define __rsnd_mod_shift_pcm_new 28 /* always called */ |
319 | #define __rsnd_mod_shift_fallback 28 /* always called */ | 326 | #define __rsnd_mod_shift_fallback 28 /* always called */ |
320 | #define __rsnd_mod_shift_hw_params 28 /* always called */ | 327 | #define __rsnd_mod_shift_hw_params 28 /* always called */ |
328 | #define __rsnd_mod_shift_pointer 28 /* always called */ | ||
321 | 329 | ||
322 | #define __rsnd_mod_add_probe 0 | 330 | #define __rsnd_mod_add_probe 0 |
323 | #define __rsnd_mod_add_remove 0 | 331 | #define __rsnd_mod_add_remove 0 |
@@ -331,6 +339,7 @@ struct rsnd_mod { | |||
331 | #define __rsnd_mod_add_pcm_new 0 | 339 | #define __rsnd_mod_add_pcm_new 0 |
332 | #define __rsnd_mod_add_fallback 0 | 340 | #define __rsnd_mod_add_fallback 0 |
333 | #define __rsnd_mod_add_hw_params 0 | 341 | #define __rsnd_mod_add_hw_params 0 |
342 | #define __rsnd_mod_add_pointer 0 | ||
334 | 343 | ||
335 | #define __rsnd_mod_call_probe 0 | 344 | #define __rsnd_mod_call_probe 0 |
336 | #define __rsnd_mod_call_remove 0 | 345 | #define __rsnd_mod_call_remove 0 |
@@ -342,6 +351,7 @@ struct rsnd_mod { | |||
342 | #define __rsnd_mod_call_pcm_new 0 | 351 | #define __rsnd_mod_call_pcm_new 0 |
343 | #define __rsnd_mod_call_fallback 0 | 352 | #define __rsnd_mod_call_fallback 0 |
344 | #define __rsnd_mod_call_hw_params 0 | 353 | #define __rsnd_mod_call_hw_params 0 |
354 | #define __rsnd_mod_call_pointer 0 | ||
345 | #define __rsnd_mod_call_nolock_start 0 | 355 | #define __rsnd_mod_call_nolock_start 0 |
346 | #define __rsnd_mod_call_nolock_stop 1 | 356 | #define __rsnd_mod_call_nolock_stop 1 |
347 | 357 | ||
@@ -420,7 +430,6 @@ struct rsnd_dai_stream { | |||
420 | char name[RSND_DAI_NAME_SIZE]; | 430 | char name[RSND_DAI_NAME_SIZE]; |
421 | struct snd_pcm_substream *substream; | 431 | struct snd_pcm_substream *substream; |
422 | struct rsnd_mod *mod[RSND_MOD_MAX]; | 432 | struct rsnd_mod *mod[RSND_MOD_MAX]; |
423 | struct rsnd_dai_path_info *info; /* rcar_snd.h */ | ||
424 | struct rsnd_dai *rdai; | 433 | struct rsnd_dai *rdai; |
425 | u32 parent_ssi_status; | 434 | u32 parent_ssi_status; |
426 | int byte_pos; | 435 | int byte_pos; |
@@ -477,7 +486,6 @@ int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); | |||
477 | int rsnd_dai_connect(struct rsnd_mod *mod, | 486 | int rsnd_dai_connect(struct rsnd_mod *mod, |
478 | struct rsnd_dai_stream *io, | 487 | struct rsnd_dai_stream *io, |
479 | enum rsnd_mod_type type); | 488 | enum rsnd_mod_type type); |
480 | #define rsnd_dai_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_DAI) | ||
481 | 489 | ||
482 | /* | 490 | /* |
483 | * R-Car Gen1/Gen2 | 491 | * R-Car Gen1/Gen2 |
@@ -648,6 +656,13 @@ int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); | |||
648 | int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); | 656 | int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); |
649 | u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io); | 657 | u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io); |
650 | 658 | ||
659 | #define RSND_SSI_HDMI_PORT0 0xf0 | ||
660 | #define RSND_SSI_HDMI_PORT1 0xf1 | ||
661 | int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io); | ||
662 | void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, | ||
663 | struct device_node *endpoint, | ||
664 | int dai_i); | ||
665 | |||
651 | #define rsnd_ssi_is_pin_sharing(io) \ | 666 | #define rsnd_ssi_is_pin_sharing(io) \ |
652 | __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) | 667 | __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) |
653 | int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); | 668 | int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); |
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 76a477a3ccb5..8dbe9ebcbff1 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c | |||
@@ -12,10 +12,6 @@ | |||
12 | 12 | ||
13 | #define SRC_NAME "src" | 13 | #define SRC_NAME "src" |
14 | 14 | ||
15 | /* SRCx_STATUS */ | ||
16 | #define OUF_SRCO ((1 << 12) | (1 << 13)) | ||
17 | #define OUF_SRCI ((1 << 9) | (1 << 8)) | ||
18 | |||
19 | /* SCU_SYSTEM_STATUS0/1 */ | 15 | /* SCU_SYSTEM_STATUS0/1 */ |
20 | #define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) | 16 | #define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) |
21 | 17 | ||
@@ -55,20 +51,6 @@ struct rsnd_src { | |||
55 | * | 51 | * |
56 | */ | 52 | */ |
57 | 53 | ||
58 | /* | ||
59 | * src.c is caring... | ||
60 | * | ||
61 | * Gen1 | ||
62 | * | ||
63 | * [mem] -> [SRU] -> [SSI] | ||
64 | * |--------| | ||
65 | * | ||
66 | * Gen2 | ||
67 | * | ||
68 | * [mem] -> [SRC] -> [SSIU] -> [SSI] | ||
69 | * |-----------------| | ||
70 | */ | ||
71 | |||
72 | static void rsnd_src_activation(struct rsnd_mod *mod) | 54 | static void rsnd_src_activation(struct rsnd_mod *mod) |
73 | { | 55 | { |
74 | rsnd_mod_write(mod, SRC_SWRSR, 0); | 56 | rsnd_mod_write(mod, SRC_SWRSR, 0); |
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 91e5c07911b4..59ca6e3f46bc 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c | |||
@@ -11,6 +11,7 @@ | |||
11 | * it under the terms of the GNU General Public License version 2 as | 11 | * it under the terms of the GNU General Public License version 2 as |
12 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
13 | */ | 13 | */ |
14 | #include <sound/simple_card_utils.h> | ||
14 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
15 | #include "rsnd.h" | 16 | #include "rsnd.h" |
16 | #define RSND_SSI_NAME_SIZE 16 | 17 | #define RSND_SSI_NAME_SIZE 16 |
@@ -81,6 +82,8 @@ struct rsnd_ssi { | |||
81 | /* flags */ | 82 | /* flags */ |
82 | #define RSND_SSI_CLK_PIN_SHARE (1 << 0) | 83 | #define RSND_SSI_CLK_PIN_SHARE (1 << 0) |
83 | #define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */ | 84 | #define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */ |
85 | #define RSND_SSI_HDMI0 (1 << 2) /* for HDMI0 */ | ||
86 | #define RSND_SSI_HDMI1 (1 << 3) /* for HDMI1 */ | ||
84 | 87 | ||
85 | #define for_each_rsnd_ssi(pos, priv, i) \ | 88 | #define for_each_rsnd_ssi(pos, priv, i) \ |
86 | for (i = 0; \ | 89 | for (i = 0; \ |
@@ -99,6 +102,20 @@ struct rsnd_ssi { | |||
99 | #define rsnd_ssi_is_run_mods(mod, io) \ | 102 | #define rsnd_ssi_is_run_mods(mod, io) \ |
100 | (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod))) | 103 | (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod))) |
101 | 104 | ||
105 | int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io) | ||
106 | { | ||
107 | struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); | ||
108 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | ||
109 | |||
110 | if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_HDMI0) | ||
111 | return RSND_SSI_HDMI_PORT0; | ||
112 | |||
113 | if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_HDMI1) | ||
114 | return RSND_SSI_HDMI_PORT1; | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
102 | int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) | 119 | int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) |
103 | { | 120 | { |
104 | struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); | 121 | struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); |
@@ -675,6 +692,17 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, | |||
675 | return ret; | 692 | return ret; |
676 | } | 693 | } |
677 | 694 | ||
695 | static int rsnd_ssi_pointer(struct rsnd_mod *mod, | ||
696 | struct rsnd_dai_stream *io, | ||
697 | snd_pcm_uframes_t *pointer) | ||
698 | { | ||
699 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | ||
700 | |||
701 | *pointer = bytes_to_frames(runtime, io->byte_pos); | ||
702 | |||
703 | return 0; | ||
704 | } | ||
705 | |||
678 | static struct rsnd_mod_ops rsnd_ssi_pio_ops = { | 706 | static struct rsnd_mod_ops rsnd_ssi_pio_ops = { |
679 | .name = SSI_NAME, | 707 | .name = SSI_NAME, |
680 | .probe = rsnd_ssi_common_probe, | 708 | .probe = rsnd_ssi_common_probe, |
@@ -683,6 +711,7 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { | |||
683 | .start = rsnd_ssi_start, | 711 | .start = rsnd_ssi_start, |
684 | .stop = rsnd_ssi_stop, | 712 | .stop = rsnd_ssi_stop, |
685 | .irq = rsnd_ssi_irq, | 713 | .irq = rsnd_ssi_irq, |
714 | .pointer= rsnd_ssi_pointer, | ||
686 | .pcm_new = rsnd_ssi_pcm_new, | 715 | .pcm_new = rsnd_ssi_pcm_new, |
687 | .hw_params = rsnd_ssi_hw_params, | 716 | .hw_params = rsnd_ssi_hw_params, |
688 | }; | 717 | }; |
@@ -787,13 +816,6 @@ int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) | |||
787 | 816 | ||
788 | 817 | ||
789 | /* | 818 | /* |
790 | * Non SSI | ||
791 | */ | ||
792 | static struct rsnd_mod_ops rsnd_ssi_non_ops = { | ||
793 | .name = SSI_NAME, | ||
794 | }; | ||
795 | |||
796 | /* | ||
797 | * ssi mod function | 819 | * ssi mod function |
798 | */ | 820 | */ |
799 | static void rsnd_ssi_connect(struct rsnd_mod *mod, | 821 | static void rsnd_ssi_connect(struct rsnd_mod *mod, |
@@ -847,6 +869,47 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, | |||
847 | of_node_put(node); | 869 | of_node_put(node); |
848 | } | 870 | } |
849 | 871 | ||
872 | static void __rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, | ||
873 | struct rsnd_dai_stream *io, | ||
874 | struct device_node *remote_ep) | ||
875 | { | ||
876 | struct device *dev = rsnd_priv_to_dev(priv); | ||
877 | struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); | ||
878 | struct rsnd_ssi *ssi; | ||
879 | |||
880 | if (!mod) | ||
881 | return; | ||
882 | |||
883 | ssi = rsnd_mod_to_ssi(mod); | ||
884 | |||
885 | if (strstr(remote_ep->full_name, "hdmi0")) { | ||
886 | ssi->flags |= RSND_SSI_HDMI0; | ||
887 | dev_dbg(dev, "%s[%d] connected to HDMI0\n", | ||
888 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
889 | } | ||
890 | |||
891 | if (strstr(remote_ep->full_name, "hdmi1")) { | ||
892 | ssi->flags |= RSND_SSI_HDMI1; | ||
893 | dev_dbg(dev, "%s[%d] connected to HDMI1\n", | ||
894 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
895 | } | ||
896 | } | ||
897 | |||
898 | void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, | ||
899 | struct device_node *endpoint, | ||
900 | int dai_i) | ||
901 | { | ||
902 | struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i); | ||
903 | struct device_node *remote_ep; | ||
904 | |||
905 | remote_ep = of_graph_get_remote_endpoint(endpoint); | ||
906 | if (!remote_ep) | ||
907 | return; | ||
908 | |||
909 | __rsnd_ssi_parse_hdmi_connection(priv, &rdai->playback, remote_ep); | ||
910 | __rsnd_ssi_parse_hdmi_connection(priv, &rdai->capture, remote_ep); | ||
911 | } | ||
912 | |||
850 | struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) | 913 | struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) |
851 | { | 914 | { |
852 | if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) | 915 | if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) |
@@ -952,7 +1015,6 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) | |||
952 | goto rsnd_ssi_probe_done; | 1015 | goto rsnd_ssi_probe_done; |
953 | } | 1016 | } |
954 | 1017 | ||
955 | ops = &rsnd_ssi_non_ops; | ||
956 | if (of_property_read_bool(np, "pio-transfer")) | 1018 | if (of_property_read_bool(np, "pio-transfer")) |
957 | ops = &rsnd_ssi_pio_ops; | 1019 | ops = &rsnd_ssi_pio_ops; |
958 | else | 1020 | else |
diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 512d238b79e2..bed2c9c0004b 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c | |||
@@ -123,6 +123,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, | |||
123 | struct rsnd_dai_stream *io, | 123 | struct rsnd_dai_stream *io, |
124 | struct rsnd_priv *priv) | 124 | struct rsnd_priv *priv) |
125 | { | 125 | { |
126 | int hdmi = rsnd_ssi_hdmi_port(io); | ||
126 | int ret; | 127 | int ret; |
127 | 128 | ||
128 | ret = rsnd_ssiu_init(mod, io, priv); | 129 | ret = rsnd_ssiu_init(mod, io, priv); |
@@ -150,6 +151,42 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, | |||
150 | rsnd_get_dalign(mod, io)); | 151 | rsnd_get_dalign(mod, io)); |
151 | } | 152 | } |
152 | 153 | ||
154 | if (hdmi) { | ||
155 | enum rsnd_mod_type rsnd_ssi_array[] = { | ||
156 | RSND_MOD_SSIM1, | ||
157 | RSND_MOD_SSIM2, | ||
158 | RSND_MOD_SSIM3, | ||
159 | }; | ||
160 | struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); | ||
161 | struct rsnd_mod *pos; | ||
162 | u32 val; | ||
163 | int i, shift; | ||
164 | |||
165 | i = rsnd_mod_id(ssi_mod); | ||
166 | |||
167 | /* output all same SSI as default */ | ||
168 | val = i << 16 | | ||
169 | i << 20 | | ||
170 | i << 24 | | ||
171 | i << 28 | | ||
172 | i; | ||
173 | |||
174 | for_each_rsnd_mod_array(i, pos, io, rsnd_ssi_array) { | ||
175 | shift = (i * 4) + 16; | ||
176 | val = (val & ~(0xF << shift)) | | ||
177 | rsnd_mod_id(pos) << shift; | ||
178 | } | ||
179 | |||
180 | switch (hdmi) { | ||
181 | case RSND_SSI_HDMI_PORT0: | ||
182 | rsnd_mod_write(mod, HDMI0_SEL, val); | ||
183 | break; | ||
184 | case RSND_SSI_HDMI_PORT1: | ||
185 | rsnd_mod_write(mod, HDMI1_SEL, val); | ||
186 | break; | ||
187 | } | ||
188 | } | ||
189 | |||
153 | return 0; | 190 | return 0; |
154 | } | 191 | } |
155 | 192 | ||
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 | } |
4045 | EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); | 4050 | EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); |
4046 | 4051 | ||
4052 | int 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 | } | ||
4086 | EXPORT_SYMBOL_GPL(snd_soc_get_dai_id); | ||
4087 | |||
4047 | int snd_soc_get_dai_name(struct of_phandle_args *args, | 4088 | int snd_soc_get_dai_name(struct of_phandle_args *args, |
4048 | const char **dai_name) | 4089 | const char **dai_name) |
4049 | { | 4090 | { |