diff options
author | Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 2016-02-25 00:55:23 -0500 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-03-04 23:27:27 -0500 |
commit | ec2ac01afe001360b911e27c915579fd003339e8 (patch) | |
tree | eede19ec99703ecf11477fe917b60369dd326650 | |
parent | eed76bb811cd143119b4bdeca88606685222e687 (diff) |
ASoC: rsnd: add CTU support
This patch adds CTU (= Channel Transfer Unit) support on
Renesas R-Car sound driver.
It can Down/Up mixing and splitter. You need to check R-Car datasheet
especially CTUn_CPMDR/CTUn_SV0xR/CTUn_SV1xR/CTUn_SV2xR/CTUn_SV3xR
for setting parameter.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | sound/soc/sh/rcar/ctu.c | 234 |
1 files changed, 197 insertions, 37 deletions
diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index b326966ea407..9dcc1f9db026 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c | |||
@@ -12,8 +12,74 @@ | |||
12 | #define CTU_NAME_SIZE 16 | 12 | #define CTU_NAME_SIZE 16 |
13 | #define CTU_NAME "ctu" | 13 | #define CTU_NAME "ctu" |
14 | 14 | ||
15 | /* | ||
16 | * User needs to setup CTU by amixer, and its settings are | ||
17 | * based on below registers | ||
18 | * | ||
19 | * CTUn_CPMDR : amixser set "CTU Pass" | ||
20 | * CTUn_SV0xR : amixser set "CTU SV0" | ||
21 | * CTUn_SV1xR : amixser set "CTU SV1" | ||
22 | * CTUn_SV2xR : amixser set "CTU SV2" | ||
23 | * CTUn_SV3xR : amixser set "CTU SV3" | ||
24 | * | ||
25 | * [CTU Pass] | ||
26 | * 0000: default | ||
27 | * 0001: Connect input data of channel 0 | ||
28 | * 0010: Connect input data of channel 1 | ||
29 | * 0011: Connect input data of channel 2 | ||
30 | * 0100: Connect input data of channel 3 | ||
31 | * 0101: Connect input data of channel 4 | ||
32 | * 0110: Connect input data of channel 5 | ||
33 | * 0111: Connect input data of channel 6 | ||
34 | * 1000: Connect input data of channel 7 | ||
35 | * 1001: Connect calculated data by scale values of matrix row 0 | ||
36 | * 1010: Connect calculated data by scale values of matrix row 1 | ||
37 | * 1011: Connect calculated data by scale values of matrix row 2 | ||
38 | * 1100: Connect calculated data by scale values of matrix row 3 | ||
39 | * | ||
40 | * [CTU SVx] | ||
41 | * [Output0] = [SV00, SV01, SV02, SV03, SV04, SV05, SV06, SV07] | ||
42 | * [Output1] = [SV10, SV11, SV12, SV13, SV14, SV15, SV16, SV17] | ||
43 | * [Output2] = [SV20, SV21, SV22, SV23, SV24, SV25, SV26, SV27] | ||
44 | * [Output3] = [SV30, SV31, SV32, SV33, SV34, SV35, SV36, SV37] | ||
45 | * [Output4] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] | ||
46 | * [Output5] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] | ||
47 | * [Output6] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] | ||
48 | * [Output7] = [ 0, 0, 0, 0, 0, 0, 0, 0 ] | ||
49 | * | ||
50 | * [SVxx] | ||
51 | * Plus Minus | ||
52 | * value time dB value time dB | ||
53 | * ----------------------------------------------------------------------- | ||
54 | * H'7F_FFFF 2 6 H'80_0000 2 6 | ||
55 | * ... | ||
56 | * H'40_0000 1 0 H'C0_0000 1 0 | ||
57 | * ... | ||
58 | * H'00_0001 2.38 x 10^-7 -132 | ||
59 | * H'00_0000 0 Mute H'FF_FFFF 2.38 x 10^-7 -132 | ||
60 | * | ||
61 | * | ||
62 | * Ex) Input ch -> Output ch | ||
63 | * 1ch -> 0ch | ||
64 | * 0ch -> 1ch | ||
65 | * | ||
66 | * amixer set "CTU Reset" on | ||
67 | * amixer set "CTU Pass" 9,10 | ||
68 | * amixer set "CTU SV0" 0,4194304 | ||
69 | * amixer set "CTU SV1" 4194304,0 | ||
70 | * or | ||
71 | * amixer set "CTU Reset" on | ||
72 | * amixer set "CTU Pass" 2,1 | ||
73 | */ | ||
74 | |||
15 | struct rsnd_ctu { | 75 | struct rsnd_ctu { |
16 | struct rsnd_mod mod; | 76 | struct rsnd_mod mod; |
77 | struct rsnd_kctrl_cfg_m pass; | ||
78 | struct rsnd_kctrl_cfg_m sv0; | ||
79 | struct rsnd_kctrl_cfg_m sv1; | ||
80 | struct rsnd_kctrl_cfg_m sv2; | ||
81 | struct rsnd_kctrl_cfg_m sv3; | ||
82 | struct rsnd_kctrl_cfg_s reset; | ||
17 | int channels; | 83 | int channels; |
18 | }; | 84 | }; |
19 | 85 | ||
@@ -58,51 +124,91 @@ static int rsnd_ctu_probe_(struct rsnd_mod *mod, | |||
58 | static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, | 124 | static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, |
59 | struct rsnd_mod *mod) | 125 | struct rsnd_mod *mod) |
60 | { | 126 | { |
127 | struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); | ||
128 | u32 cpmdr = 0; | ||
129 | u32 scmdr = 0; | ||
130 | int i; | ||
131 | |||
132 | for (i = 0; i < RSND_MAX_CHANNELS; i++) { | ||
133 | u32 val = ctu->pass.val[i]; | ||
134 | |||
135 | cpmdr |= val << (28 - (i * 4)); | ||
136 | |||
137 | if ((val > 0x8) && (scmdr < (val - 0x8))) | ||
138 | scmdr = val - 0x8; | ||
139 | } | ||
140 | |||
61 | rsnd_mod_write(mod, CTU_CTUIR, 1); | 141 | rsnd_mod_write(mod, CTU_CTUIR, 1); |
62 | 142 | ||
63 | rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io)); | 143 | rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io)); |
64 | 144 | ||
65 | rsnd_mod_write(mod, CTU_CPMDR, 0); | 145 | rsnd_mod_write(mod, CTU_CPMDR, cpmdr); |
66 | rsnd_mod_write(mod, CTU_SCMDR, 0); | 146 | |
67 | rsnd_mod_write(mod, CTU_SV00R, 0); | 147 | rsnd_mod_write(mod, CTU_SCMDR, scmdr); |
68 | rsnd_mod_write(mod, CTU_SV01R, 0); | 148 | |
69 | rsnd_mod_write(mod, CTU_SV02R, 0); | 149 | if (scmdr > 0) { |
70 | rsnd_mod_write(mod, CTU_SV03R, 0); | 150 | rsnd_mod_write(mod, CTU_SV00R, ctu->sv0.val[0]); |
71 | rsnd_mod_write(mod, CTU_SV04R, 0); | 151 | rsnd_mod_write(mod, CTU_SV01R, ctu->sv0.val[1]); |
72 | rsnd_mod_write(mod, CTU_SV05R, 0); | 152 | rsnd_mod_write(mod, CTU_SV02R, ctu->sv0.val[2]); |
73 | rsnd_mod_write(mod, CTU_SV06R, 0); | 153 | rsnd_mod_write(mod, CTU_SV03R, ctu->sv0.val[3]); |
74 | rsnd_mod_write(mod, CTU_SV07R, 0); | 154 | rsnd_mod_write(mod, CTU_SV04R, ctu->sv0.val[4]); |
75 | 155 | rsnd_mod_write(mod, CTU_SV05R, ctu->sv0.val[5]); | |
76 | rsnd_mod_write(mod, CTU_SV10R, 0); | 156 | rsnd_mod_write(mod, CTU_SV06R, ctu->sv0.val[6]); |
77 | rsnd_mod_write(mod, CTU_SV11R, 0); | 157 | rsnd_mod_write(mod, CTU_SV07R, ctu->sv0.val[7]); |
78 | rsnd_mod_write(mod, CTU_SV12R, 0); | 158 | } |
79 | rsnd_mod_write(mod, CTU_SV13R, 0); | 159 | if (scmdr > 1) { |
80 | rsnd_mod_write(mod, CTU_SV14R, 0); | 160 | rsnd_mod_write(mod, CTU_SV10R, ctu->sv1.val[0]); |
81 | rsnd_mod_write(mod, CTU_SV15R, 0); | 161 | rsnd_mod_write(mod, CTU_SV11R, ctu->sv1.val[1]); |
82 | rsnd_mod_write(mod, CTU_SV16R, 0); | 162 | rsnd_mod_write(mod, CTU_SV12R, ctu->sv1.val[2]); |
83 | rsnd_mod_write(mod, CTU_SV17R, 0); | 163 | rsnd_mod_write(mod, CTU_SV13R, ctu->sv1.val[3]); |
84 | 164 | rsnd_mod_write(mod, CTU_SV14R, ctu->sv1.val[4]); | |
85 | rsnd_mod_write(mod, CTU_SV20R, 0); | 165 | rsnd_mod_write(mod, CTU_SV15R, ctu->sv1.val[5]); |
86 | rsnd_mod_write(mod, CTU_SV21R, 0); | 166 | rsnd_mod_write(mod, CTU_SV16R, ctu->sv1.val[6]); |
87 | rsnd_mod_write(mod, CTU_SV22R, 0); | 167 | rsnd_mod_write(mod, CTU_SV17R, ctu->sv1.val[7]); |
88 | rsnd_mod_write(mod, CTU_SV23R, 0); | 168 | } |
89 | rsnd_mod_write(mod, CTU_SV24R, 0); | 169 | if (scmdr > 2) { |
90 | rsnd_mod_write(mod, CTU_SV25R, 0); | 170 | rsnd_mod_write(mod, CTU_SV20R, ctu->sv2.val[0]); |
91 | rsnd_mod_write(mod, CTU_SV26R, 0); | 171 | rsnd_mod_write(mod, CTU_SV21R, ctu->sv2.val[1]); |
92 | rsnd_mod_write(mod, CTU_SV27R, 0); | 172 | rsnd_mod_write(mod, CTU_SV22R, ctu->sv2.val[2]); |
93 | 173 | rsnd_mod_write(mod, CTU_SV23R, ctu->sv2.val[3]); | |
94 | rsnd_mod_write(mod, CTU_SV30R, 0); | 174 | rsnd_mod_write(mod, CTU_SV24R, ctu->sv2.val[4]); |
95 | rsnd_mod_write(mod, CTU_SV31R, 0); | 175 | rsnd_mod_write(mod, CTU_SV25R, ctu->sv2.val[5]); |
96 | rsnd_mod_write(mod, CTU_SV32R, 0); | 176 | rsnd_mod_write(mod, CTU_SV26R, ctu->sv2.val[6]); |
97 | rsnd_mod_write(mod, CTU_SV33R, 0); | 177 | rsnd_mod_write(mod, CTU_SV27R, ctu->sv2.val[7]); |
98 | rsnd_mod_write(mod, CTU_SV34R, 0); | 178 | } |
99 | rsnd_mod_write(mod, CTU_SV35R, 0); | 179 | if (scmdr > 3) { |
100 | rsnd_mod_write(mod, CTU_SV36R, 0); | 180 | rsnd_mod_write(mod, CTU_SV30R, ctu->sv3.val[0]); |
101 | rsnd_mod_write(mod, CTU_SV37R, 0); | 181 | rsnd_mod_write(mod, CTU_SV31R, ctu->sv3.val[1]); |
182 | rsnd_mod_write(mod, CTU_SV32R, ctu->sv3.val[2]); | ||
183 | rsnd_mod_write(mod, CTU_SV33R, ctu->sv3.val[3]); | ||
184 | rsnd_mod_write(mod, CTU_SV34R, ctu->sv3.val[4]); | ||
185 | rsnd_mod_write(mod, CTU_SV35R, ctu->sv3.val[5]); | ||
186 | rsnd_mod_write(mod, CTU_SV36R, ctu->sv3.val[6]); | ||
187 | rsnd_mod_write(mod, CTU_SV37R, ctu->sv3.val[7]); | ||
188 | } | ||
102 | 189 | ||
103 | rsnd_mod_write(mod, CTU_CTUIR, 0); | 190 | rsnd_mod_write(mod, CTU_CTUIR, 0); |
104 | } | 191 | } |
105 | 192 | ||
193 | static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io, | ||
194 | struct rsnd_mod *mod) | ||
195 | { | ||
196 | struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); | ||
197 | int i; | ||
198 | |||
199 | if (!ctu->reset.val) | ||
200 | return; | ||
201 | |||
202 | for (i = 0; i < RSND_MAX_CHANNELS; i++) { | ||
203 | ctu->pass.val[i] = 0; | ||
204 | ctu->sv0.val[i] = 0; | ||
205 | ctu->sv1.val[i] = 0; | ||
206 | ctu->sv2.val[i] = 0; | ||
207 | ctu->sv3.val[i] = 0; | ||
208 | } | ||
209 | ctu->reset.val = 0; | ||
210 | } | ||
211 | |||
106 | static int rsnd_ctu_init(struct rsnd_mod *mod, | 212 | static int rsnd_ctu_init(struct rsnd_mod *mod, |
107 | struct rsnd_dai_stream *io, | 213 | struct rsnd_dai_stream *io, |
108 | struct rsnd_priv *priv) | 214 | struct rsnd_priv *priv) |
@@ -164,12 +270,66 @@ static int rsnd_ctu_hw_params(struct rsnd_mod *mod, | |||
164 | return 0; | 270 | return 0; |
165 | } | 271 | } |
166 | 272 | ||
273 | static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, | ||
274 | struct rsnd_dai_stream *io, | ||
275 | struct snd_soc_pcm_runtime *rtd) | ||
276 | { | ||
277 | struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); | ||
278 | int ret; | ||
279 | |||
280 | /* CTU Pass */ | ||
281 | ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass", | ||
282 | NULL, | ||
283 | &ctu->pass, RSND_MAX_CHANNELS, | ||
284 | 0xC); | ||
285 | |||
286 | /* ROW0 */ | ||
287 | ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0", | ||
288 | NULL, | ||
289 | &ctu->sv0, RSND_MAX_CHANNELS, | ||
290 | 0x00FFFFFF); | ||
291 | if (ret < 0) | ||
292 | return ret; | ||
293 | |||
294 | /* ROW1 */ | ||
295 | ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1", | ||
296 | NULL, | ||
297 | &ctu->sv1, RSND_MAX_CHANNELS, | ||
298 | 0x00FFFFFF); | ||
299 | if (ret < 0) | ||
300 | return ret; | ||
301 | |||
302 | /* ROW2 */ | ||
303 | ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2", | ||
304 | NULL, | ||
305 | &ctu->sv2, RSND_MAX_CHANNELS, | ||
306 | 0x00FFFFFF); | ||
307 | if (ret < 0) | ||
308 | return ret; | ||
309 | |||
310 | /* ROW3 */ | ||
311 | ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3", | ||
312 | NULL, | ||
313 | &ctu->sv3, RSND_MAX_CHANNELS, | ||
314 | 0x00FFFFFF); | ||
315 | if (ret < 0) | ||
316 | return ret; | ||
317 | |||
318 | /* Reset */ | ||
319 | ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset", | ||
320 | rsnd_ctu_value_reset, | ||
321 | &ctu->reset, 1); | ||
322 | |||
323 | return ret; | ||
324 | } | ||
325 | |||
167 | static struct rsnd_mod_ops rsnd_ctu_ops = { | 326 | static struct rsnd_mod_ops rsnd_ctu_ops = { |
168 | .name = CTU_NAME, | 327 | .name = CTU_NAME, |
169 | .probe = rsnd_ctu_probe_, | 328 | .probe = rsnd_ctu_probe_, |
170 | .init = rsnd_ctu_init, | 329 | .init = rsnd_ctu_init, |
171 | .quit = rsnd_ctu_quit, | 330 | .quit = rsnd_ctu_quit, |
172 | .hw_params = rsnd_ctu_hw_params, | 331 | .hw_params = rsnd_ctu_hw_params, |
332 | .pcm_new = rsnd_ctu_pcm_new, | ||
173 | }; | 333 | }; |
174 | 334 | ||
175 | struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) | 335 | struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) |