]>
Commit | Line | Data |
---|---|---|
70fb1052 KM |
1 | /* |
2 | * mix.c | |
3 | * | |
4 | * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | */ | |
3e3c9ee1 KM |
10 | |
11 | /* | |
12 | * CTUn MIXn | |
13 | * +------+ +------+ | |
14 | * [SRC3 / SRC6] -> |CTU n0| -> [MIX n0| -> | |
15 | * [SRC4 / SRC9] -> |CTU n1| -> [MIX n1| -> | |
16 | * [SRC0 / SRC1] -> |CTU n2| -> [MIX n2| -> | |
17 | * [SRC2 / SRC5] -> |CTU n3| -> [MIX n3| -> | |
18 | * +------+ +------+ | |
19 | * | |
20 | * ex) | |
21 | * DAI0 : playback = <&src0 &ctu02 &mix0 &dvc0 &ssi0>; | |
22 | * DAI1 : playback = <&src2 &ctu03 &mix0 &dvc0 &ssi0>; | |
23 | * | |
24 | * MIX Volume | |
25 | * amixer set "MIX",0 100% // DAI0 Volume | |
26 | * amixer set "MIX",1 100% // DAI1 Volume | |
27 | * | |
28 | * Volume Ramp | |
29 | * amixer set "MIX Ramp Up Rate" "0.125 dB/1 step" | |
30 | * amixer set "MIX Ramp Down Rate" "4 dB/1 step" | |
31 | * amixer set "MIX Ramp" on | |
32 | * aplay xxx.wav & | |
33 | * amixer set "MIX",0 80% // DAI0 Volume Down | |
34 | * amixer set "MIX",1 100% // DAI1 Volume Up | |
35 | */ | |
36 | ||
70fb1052 KM |
37 | #include "rsnd.h" |
38 | ||
39 | #define MIX_NAME_SIZE 16 | |
40 | #define MIX_NAME "mix" | |
41 | ||
42 | struct rsnd_mix { | |
70fb1052 | 43 | struct rsnd_mod mod; |
3e3c9ee1 KM |
44 | struct rsnd_kctrl_cfg_s volumeA; /* MDBAR */ |
45 | struct rsnd_kctrl_cfg_s volumeB; /* MDBBR */ | |
46 | struct rsnd_kctrl_cfg_s volumeC; /* MDBCR */ | |
47 | struct rsnd_kctrl_cfg_s volumeD; /* MDBDR */ | |
48 | struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */ | |
49 | struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */ | |
50 | struct rsnd_kctrl_cfg_s rdw; /* Ramp Rate Down */ | |
51 | u32 flags; | |
70fb1052 KM |
52 | }; |
53 | ||
3e3c9ee1 KM |
54 | #define ONCE_KCTRL_INITIALIZED (1 << 0) |
55 | #define HAS_VOLA (1 << 1) | |
56 | #define HAS_VOLB (1 << 2) | |
57 | #define HAS_VOLC (1 << 3) | |
58 | #define HAS_VOLD (1 << 4) | |
59 | ||
60 | #define VOL_MAX 0x3ff | |
61 | ||
62 | #define rsnd_mod_to_mix(_mod) \ | |
63 | container_of((_mod), struct rsnd_mix, mod) | |
64 | ||
c7fe4be8 | 65 | #define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id) |
70fb1052 KM |
66 | #define rsnd_mix_nr(priv) ((priv)->mix_nr) |
67 | #define for_each_rsnd_mix(pos, priv, i) \ | |
68 | for ((i) = 0; \ | |
69 | ((i) < rsnd_mix_nr(priv)) && \ | |
70 | ((pos) = (struct rsnd_mix *)(priv)->mix + i); \ | |
71 | i++) | |
72 | ||
4fe32521 | 73 | static void rsnd_mix_activation(struct rsnd_mod *mod) |
70fb1052 KM |
74 | { |
75 | rsnd_mod_write(mod, MIX_SWRSR, 0); | |
76 | rsnd_mod_write(mod, MIX_SWRSR, 1); | |
77 | } | |
78 | ||
95e6b0dd KM |
79 | static void rsnd_mix_halt(struct rsnd_mod *mod) |
80 | { | |
81 | rsnd_mod_write(mod, MIX_MIXIR, 1); | |
82 | rsnd_mod_write(mod, MIX_SWRSR, 0); | |
83 | } | |
84 | ||
3e3c9ee1 KM |
85 | #define rsnd_mix_get_vol(mix, X) \ |
86 | rsnd_flags_has(mix, HAS_VOL##X) ? \ | |
3a9fa27b | 87 | (VOL_MAX - rsnd_kctrl_vals(mix->volume##X)) : 0 |
13e0d17d KM |
88 | static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io, |
89 | struct rsnd_mod *mod) | |
70fb1052 | 90 | { |
3e3c9ee1 KM |
91 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
92 | struct device *dev = rsnd_priv_to_dev(priv); | |
93 | struct rsnd_mix *mix = rsnd_mod_to_mix(mod); | |
94 | u32 volA = rsnd_mix_get_vol(mix, A); | |
95 | u32 volB = rsnd_mix_get_vol(mix, B); | |
96 | u32 volC = rsnd_mix_get_vol(mix, C); | |
97 | u32 volD = rsnd_mix_get_vol(mix, D); | |
98 | ||
99 | dev_dbg(dev, "MIX A/B/C/D = %02x/%02x/%02x/%02x\n", | |
100 | volA, volB, volC, volD); | |
101 | ||
102 | rsnd_mod_write(mod, MIX_MDBAR, volA); | |
103 | rsnd_mod_write(mod, MIX_MDBBR, volB); | |
104 | rsnd_mod_write(mod, MIX_MDBCR, volC); | |
105 | rsnd_mod_write(mod, MIX_MDBDR, volD); | |
13e0d17d KM |
106 | } |
107 | ||
108 | static void rsnd_mix_volume_init(struct rsnd_dai_stream *io, | |
109 | struct rsnd_mod *mod) | |
110 | { | |
3e3c9ee1 KM |
111 | struct rsnd_mix *mix = rsnd_mod_to_mix(mod); |
112 | ||
13e0d17d KM |
113 | rsnd_mod_write(mod, MIX_MIXIR, 1); |
114 | ||
115 | /* General Information */ | |
eed76bb8 | 116 | rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io)); |
13e0d17d KM |
117 | |
118 | /* volume step */ | |
3a9fa27b KM |
119 | rsnd_mod_write(mod, MIX_MIXMR, rsnd_kctrl_vals(mix->ren)); |
120 | rsnd_mod_write(mod, MIX_MVPDR, rsnd_kctrl_vals(mix->rup) << 8 | | |
121 | rsnd_kctrl_vals(mix->rdw)); | |
13e0d17d KM |
122 | |
123 | /* common volume parameter */ | |
124 | rsnd_mix_volume_parameter(io, mod); | |
125 | ||
126 | rsnd_mod_write(mod, MIX_MIXIR, 0); | |
70fb1052 KM |
127 | } |
128 | ||
129 | static void rsnd_mix_volume_update(struct rsnd_dai_stream *io, | |
130 | struct rsnd_mod *mod) | |
131 | { | |
70fb1052 KM |
132 | /* Disable MIX dB setting */ |
133 | rsnd_mod_write(mod, MIX_MDBER, 0); | |
134 | ||
13e0d17d KM |
135 | /* common volume parameter */ |
136 | rsnd_mix_volume_parameter(io, mod); | |
70fb1052 KM |
137 | |
138 | /* Enable MIX dB setting */ | |
139 | rsnd_mod_write(mod, MIX_MDBER, 1); | |
140 | } | |
141 | ||
1b2ca0ad KM |
142 | static int rsnd_mix_probe_(struct rsnd_mod *mod, |
143 | struct rsnd_dai_stream *io, | |
144 | struct rsnd_priv *priv) | |
145 | { | |
146 | return rsnd_cmd_attach(io, rsnd_mod_id(mod)); | |
147 | } | |
148 | ||
70fb1052 KM |
149 | static int rsnd_mix_init(struct rsnd_mod *mod, |
150 | struct rsnd_dai_stream *io, | |
151 | struct rsnd_priv *priv) | |
152 | { | |
c9929345 | 153 | rsnd_mod_power_on(mod); |
70fb1052 | 154 | |
4fe32521 | 155 | rsnd_mix_activation(mod); |
70fb1052 | 156 | |
13e0d17d | 157 | rsnd_mix_volume_init(io, mod); |
70fb1052 KM |
158 | |
159 | rsnd_mix_volume_update(io, mod); | |
160 | ||
70fb1052 KM |
161 | return 0; |
162 | } | |
163 | ||
164 | static int rsnd_mix_quit(struct rsnd_mod *mod, | |
165 | struct rsnd_dai_stream *io, | |
166 | struct rsnd_priv *priv) | |
167 | { | |
95e6b0dd KM |
168 | rsnd_mix_halt(mod); |
169 | ||
c9929345 | 170 | rsnd_mod_power_off(mod); |
70fb1052 KM |
171 | |
172 | return 0; | |
173 | } | |
174 | ||
3e3c9ee1 KM |
175 | static int rsnd_mix_pcm_new(struct rsnd_mod *mod, |
176 | struct rsnd_dai_stream *io, | |
177 | struct snd_soc_pcm_runtime *rtd) | |
178 | { | |
179 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | |
180 | struct device *dev = rsnd_priv_to_dev(priv); | |
181 | struct rsnd_mix *mix = rsnd_mod_to_mix(mod); | |
182 | struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); | |
183 | struct rsnd_kctrl_cfg_s *volume; | |
184 | int ret; | |
185 | ||
186 | switch (rsnd_mod_id(src_mod)) { | |
187 | case 3: | |
188 | case 6: /* MDBAR */ | |
189 | volume = &mix->volumeA; | |
190 | rsnd_flags_set(mix, HAS_VOLA); | |
191 | break; | |
192 | case 4: | |
193 | case 9: /* MDBBR */ | |
194 | volume = &mix->volumeB; | |
195 | rsnd_flags_set(mix, HAS_VOLB); | |
196 | break; | |
197 | case 0: | |
198 | case 1: /* MDBCR */ | |
199 | volume = &mix->volumeC; | |
200 | rsnd_flags_set(mix, HAS_VOLC); | |
201 | break; | |
202 | case 2: | |
203 | case 5: /* MDBDR */ | |
204 | volume = &mix->volumeD; | |
205 | rsnd_flags_set(mix, HAS_VOLD); | |
206 | break; | |
207 | default: | |
208 | dev_err(dev, "unknown SRC is connected\n"); | |
209 | return -EINVAL; | |
210 | } | |
211 | ||
212 | /* Volume */ | |
213 | ret = rsnd_kctrl_new_s(mod, io, rtd, | |
214 | "MIX Playback Volume", | |
215 | rsnd_kctrl_accept_anytime, | |
216 | rsnd_mix_volume_update, | |
217 | volume, VOL_MAX); | |
218 | if (ret < 0) | |
219 | return ret; | |
3a9fa27b | 220 | rsnd_kctrl_vals(*volume) = VOL_MAX; |
3e3c9ee1 KM |
221 | |
222 | if (rsnd_flags_has(mix, ONCE_KCTRL_INITIALIZED)) | |
223 | return ret; | |
224 | ||
225 | /* Ramp */ | |
226 | ret = rsnd_kctrl_new_s(mod, io, rtd, | |
227 | "MIX Ramp Switch", | |
228 | rsnd_kctrl_accept_anytime, | |
229 | rsnd_mix_volume_update, | |
230 | &mix->ren, 1); | |
231 | if (ret < 0) | |
232 | return ret; | |
233 | ||
234 | ret = rsnd_kctrl_new_e(mod, io, rtd, | |
235 | "MIX Ramp Up Rate", | |
236 | rsnd_kctrl_accept_anytime, | |
237 | rsnd_mix_volume_update, | |
238 | &mix->rup, | |
239 | volume_ramp_rate, | |
240 | VOLUME_RAMP_MAX_MIX); | |
241 | if (ret < 0) | |
242 | return ret; | |
243 | ||
244 | ret = rsnd_kctrl_new_e(mod, io, rtd, | |
245 | "MIX Ramp Down Rate", | |
246 | rsnd_kctrl_accept_anytime, | |
247 | rsnd_mix_volume_update, | |
248 | &mix->rdw, | |
249 | volume_ramp_rate, | |
250 | VOLUME_RAMP_MAX_MIX); | |
251 | ||
252 | rsnd_flags_set(mix, ONCE_KCTRL_INITIALIZED); | |
253 | ||
254 | return ret; | |
255 | } | |
256 | ||
70fb1052 KM |
257 | static struct rsnd_mod_ops rsnd_mix_ops = { |
258 | .name = MIX_NAME, | |
1b2ca0ad | 259 | .probe = rsnd_mix_probe_, |
70fb1052 KM |
260 | .init = rsnd_mix_init, |
261 | .quit = rsnd_mix_quit, | |
3e3c9ee1 | 262 | .pcm_new = rsnd_mix_pcm_new, |
70fb1052 KM |
263 | }; |
264 | ||
265 | struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) | |
266 | { | |
267 | if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv))) | |
268 | id = 0; | |
269 | ||
c7fe4be8 | 270 | return rsnd_mod_get(rsnd_mix_get(priv, id)); |
70fb1052 KM |
271 | } |
272 | ||
2ea6b074 | 273 | int rsnd_mix_probe(struct rsnd_priv *priv) |
70fb1052 | 274 | { |
c7fe4be8 KM |
275 | struct device_node *node; |
276 | struct device_node *np; | |
70fb1052 KM |
277 | struct device *dev = rsnd_priv_to_dev(priv); |
278 | struct rsnd_mix *mix; | |
279 | struct clk *clk; | |
280 | char name[MIX_NAME_SIZE]; | |
281 | int i, nr, ret; | |
282 | ||
283 | /* This driver doesn't support Gen1 at this point */ | |
8a98b422 KM |
284 | if (rsnd_is_gen1(priv)) |
285 | return 0; | |
70fb1052 | 286 | |
c7fe4be8 KM |
287 | node = rsnd_mix_of_node(priv); |
288 | if (!node) | |
289 | return 0; /* not used is not error */ | |
70fb1052 | 290 | |
c7fe4be8 KM |
291 | nr = of_get_child_count(node); |
292 | if (!nr) { | |
293 | ret = -EINVAL; | |
294 | goto rsnd_mix_probe_done; | |
295 | } | |
70fb1052 KM |
296 | |
297 | mix = devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL); | |
c7fe4be8 KM |
298 | if (!mix) { |
299 | ret = -ENOMEM; | |
300 | goto rsnd_mix_probe_done; | |
301 | } | |
70fb1052 KM |
302 | |
303 | priv->mix_nr = nr; | |
304 | priv->mix = mix; | |
305 | ||
c7fe4be8 | 306 | i = 0; |
2b235a3d | 307 | ret = 0; |
c7fe4be8 KM |
308 | for_each_child_of_node(node, np) { |
309 | mix = rsnd_mix_get(priv, i); | |
310 | ||
70fb1052 KM |
311 | snprintf(name, MIX_NAME_SIZE, "%s.%d", |
312 | MIX_NAME, i); | |
313 | ||
314 | clk = devm_clk_get(dev, name); | |
c7fe4be8 KM |
315 | if (IS_ERR(clk)) { |
316 | ret = PTR_ERR(clk); | |
53ba2aa3 | 317 | of_node_put(np); |
c7fe4be8 KM |
318 | goto rsnd_mix_probe_done; |
319 | } | |
70fb1052 | 320 | |
b76e218a | 321 | ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, |
5ba17b42 | 322 | clk, rsnd_mod_get_status, RSND_MOD_MIX, i); |
53ba2aa3 JL |
323 | if (ret) { |
324 | of_node_put(np); | |
c7fe4be8 | 325 | goto rsnd_mix_probe_done; |
53ba2aa3 | 326 | } |
c7fe4be8 KM |
327 | |
328 | i++; | |
70fb1052 KM |
329 | } |
330 | ||
c7fe4be8 KM |
331 | rsnd_mix_probe_done: |
332 | of_node_put(node); | |
333 | ||
334 | return ret; | |
70fb1052 KM |
335 | } |
336 | ||
2ea6b074 | 337 | void rsnd_mix_remove(struct rsnd_priv *priv) |
70fb1052 KM |
338 | { |
339 | struct rsnd_mix *mix; | |
340 | int i; | |
341 | ||
342 | for_each_rsnd_mix(mix, priv, i) { | |
b76e218a | 343 | rsnd_mod_quit(rsnd_mod_get(mix)); |
70fb1052 KM |
344 | } |
345 | } |