]>
Commit | Line | Data |
---|---|---|
bff58ea4 KM |
1 | /* |
2 | * Renesas R-Car DVC support | |
3 | * | |
4 | * Copyright (C) 2014 Renesas Solutions Corp. | |
5 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
11 | #include "rsnd.h" | |
12 | ||
13 | #define RSND_DVC_NAME_SIZE 16 | |
8aefda50 KM |
14 | |
15 | #define DVC_NAME "dvc" | |
16 | ||
bff58ea4 KM |
17 | struct rsnd_dvc { |
18 | struct rsnd_dvc_platform_info *info; /* rcar_snd.h */ | |
19 | struct rsnd_mod mod; | |
170a2497 KM |
20 | struct rsnd_kctrl_cfg_m volume; |
21 | struct rsnd_kctrl_cfg_m mute; | |
22 | struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */ | |
23 | struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */ | |
24 | struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ | |
bff58ea4 KM |
25 | }; |
26 | ||
93b986e2 KM |
27 | #define rsnd_dvc_of_node(priv) \ |
28 | of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc") | |
29 | ||
bff58ea4 KM |
30 | #define rsnd_mod_to_dvc(_mod) \ |
31 | container_of((_mod), struct rsnd_dvc, mod) | |
32 | ||
33 | #define for_each_rsnd_dvc(pos, priv, i) \ | |
34 | for ((i) = 0; \ | |
35 | ((i) < rsnd_dvc_nr(priv)) && \ | |
36 | ((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \ | |
37 | i++) | |
38 | ||
2f4b1e6b | 39 | static const char * const dvc_ramp_rate[] = { |
3539cacf KM |
40 | "128 dB/1 step", /* 00000 */ |
41 | "64 dB/1 step", /* 00001 */ | |
42 | "32 dB/1 step", /* 00010 */ | |
43 | "16 dB/1 step", /* 00011 */ | |
44 | "8 dB/1 step", /* 00100 */ | |
45 | "4 dB/1 step", /* 00101 */ | |
46 | "2 dB/1 step", /* 00110 */ | |
47 | "1 dB/1 step", /* 00111 */ | |
48 | "0.5 dB/1 step", /* 01000 */ | |
49 | "0.25 dB/1 step", /* 01001 */ | |
50 | "0.125 dB/1 step", /* 01010 */ | |
51 | "0.125 dB/2 steps", /* 01011 */ | |
52 | "0.125 dB/4 steps", /* 01100 */ | |
53 | "0.125 dB/8 steps", /* 01101 */ | |
54 | "0.125 dB/16 steps", /* 01110 */ | |
55 | "0.125 dB/32 steps", /* 01111 */ | |
56 | "0.125 dB/64 steps", /* 10000 */ | |
57 | "0.125 dB/128 steps", /* 10001 */ | |
58 | "0.125 dB/256 steps", /* 10010 */ | |
59 | "0.125 dB/512 steps", /* 10011 */ | |
60 | "0.125 dB/1024 steps", /* 10100 */ | |
61 | "0.125 dB/2048 steps", /* 10101 */ | |
62 | "0.125 dB/4096 steps", /* 10110 */ | |
63 | "0.125 dB/8192 steps", /* 10111 */ | |
64 | }; | |
65 | ||
b65a7ccc KM |
66 | static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io, |
67 | struct rsnd_mod *mod) | |
bff58ea4 KM |
68 | { |
69 | struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); | |
3539cacf | 70 | u32 val[RSND_DVC_CHANNELS]; |
1c5d1c98 | 71 | u32 dvucr = 0; |
cd2b6574 | 72 | u32 mute = 0; |
bff58ea4 KM |
73 | int i; |
74 | ||
ec14af91 KM |
75 | for (i = 0; i < dvc->mute.cfg.size; i++) |
76 | mute |= (!!dvc->mute.cfg.val[i]) << i; | |
bff58ea4 | 77 | |
140bab89 KM |
78 | /* Disable DVC Register access */ |
79 | rsnd_mod_write(mod, DVC_DVUER, 0); | |
80 | ||
3539cacf KM |
81 | /* Enable Ramp */ |
82 | if (dvc->ren.val) { | |
83 | dvucr |= 0x10; | |
84 | ||
85 | /* Digital Volume Max */ | |
86 | for (i = 0; i < RSND_DVC_CHANNELS; i++) | |
87 | val[i] = dvc->volume.cfg.max; | |
88 | ||
89 | rsnd_mod_write(mod, DVC_VRCTR, 0xff); | |
90 | rsnd_mod_write(mod, DVC_VRPDR, dvc->rup.val << 8 | | |
91 | dvc->rdown.val); | |
92 | /* | |
93 | * FIXME !! | |
94 | * use scale-downed Digital Volume | |
95 | * as Volume Ramp | |
96 | * 7F FFFF -> 3FF | |
97 | */ | |
98 | rsnd_mod_write(mod, DVC_VRDBR, | |
99 | 0x3ff - (dvc->volume.val[0] >> 13)); | |
100 | ||
101 | } else { | |
102 | for (i = 0; i < RSND_DVC_CHANNELS; i++) | |
103 | val[i] = dvc->volume.val[i]; | |
104 | } | |
105 | ||
1c5d1c98 | 106 | /* Enable Digital Volume */ |
3539cacf KM |
107 | dvucr |= 0x100; |
108 | rsnd_mod_write(mod, DVC_VOL0R, val[0]); | |
109 | rsnd_mod_write(mod, DVC_VOL1R, val[1]); | |
cd2b6574 | 110 | |
1c5d1c98 KM |
111 | /* Enable Mute */ |
112 | if (mute) { | |
113 | dvucr |= 0x1; | |
114 | rsnd_mod_write(mod, DVC_ZCMCR, mute); | |
115 | } | |
116 | ||
117 | rsnd_mod_write(mod, DVC_DVUCR, dvucr); | |
140bab89 KM |
118 | |
119 | /* Enable DVC Register access */ | |
120 | rsnd_mod_write(mod, DVC_DVUER, 1); | |
bff58ea4 KM |
121 | } |
122 | ||
d1f83d6e | 123 | static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, |
2c0fac19 | 124 | struct rsnd_dai_stream *io, |
d1f83d6e KM |
125 | struct rsnd_priv *priv) |
126 | { | |
127 | struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); | |
128 | ||
129 | rsnd_kctrl_remove(dvc->volume); | |
130 | rsnd_kctrl_remove(dvc->mute); | |
131 | rsnd_kctrl_remove(dvc->ren); | |
132 | rsnd_kctrl_remove(dvc->rup); | |
133 | rsnd_kctrl_remove(dvc->rdown); | |
134 | ||
135 | return 0; | |
136 | } | |
137 | ||
bff58ea4 | 138 | static int rsnd_dvc_init(struct rsnd_mod *dvc_mod, |
2c0fac19 | 139 | struct rsnd_dai_stream *io, |
690602fc | 140 | struct rsnd_priv *priv) |
bff58ea4 | 141 | { |
bff58ea4 KM |
142 | struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); |
143 | struct device *dev = rsnd_priv_to_dev(priv); | |
144 | int dvc_id = rsnd_mod_id(dvc_mod); | |
145 | int src_id = rsnd_mod_id(src_mod); | |
146 | u32 route[] = { | |
147 | [0] = 0x30000, | |
148 | [1] = 0x30001, | |
149 | [2] = 0x40000, | |
150 | [3] = 0x10000, | |
151 | [4] = 0x20000, | |
152 | [5] = 0x40100 | |
153 | }; | |
154 | ||
155 | if (src_id >= ARRAY_SIZE(route)) { | |
156 | dev_err(dev, "DVC%d isn't connected to SRC%d\n", dvc_id, src_id); | |
157 | return -EINVAL; | |
158 | } | |
159 | ||
85642952 | 160 | rsnd_mod_hw_start(dvc_mod); |
bff58ea4 KM |
161 | |
162 | /* | |
163 | * fixme | |
164 | * it doesn't support CTU/MIX | |
165 | */ | |
166 | rsnd_mod_write(dvc_mod, CMD_ROUTE_SLCT, route[src_id]); | |
167 | ||
168 | rsnd_mod_write(dvc_mod, DVC_SWRSR, 0); | |
169 | rsnd_mod_write(dvc_mod, DVC_SWRSR, 1); | |
170 | ||
171 | rsnd_mod_write(dvc_mod, DVC_DVUIR, 1); | |
172 | ||
4e2639ff | 173 | rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod, io)); |
bff58ea4 | 174 | |
bff58ea4 | 175 | /* ch0/ch1 Volume */ |
b65a7ccc | 176 | rsnd_dvc_volume_update(io, dvc_mod); |
bff58ea4 KM |
177 | |
178 | rsnd_mod_write(dvc_mod, DVC_DVUIR, 0); | |
179 | ||
f708d944 | 180 | rsnd_adg_set_cmd_timsel_gen2(dvc_mod, io); |
bff58ea4 KM |
181 | |
182 | return 0; | |
183 | } | |
184 | ||
185 | static int rsnd_dvc_quit(struct rsnd_mod *mod, | |
2c0fac19 | 186 | struct rsnd_dai_stream *io, |
690602fc | 187 | struct rsnd_priv *priv) |
bff58ea4 | 188 | { |
85642952 | 189 | rsnd_mod_hw_stop(mod); |
bff58ea4 KM |
190 | |
191 | return 0; | |
192 | } | |
193 | ||
194 | static int rsnd_dvc_start(struct rsnd_mod *mod, | |
2c0fac19 | 195 | struct rsnd_dai_stream *io, |
690602fc | 196 | struct rsnd_priv *priv) |
bff58ea4 KM |
197 | { |
198 | rsnd_mod_write(mod, CMD_CTRL, 0x10); | |
199 | ||
200 | return 0; | |
201 | } | |
202 | ||
203 | static int rsnd_dvc_stop(struct rsnd_mod *mod, | |
2c0fac19 | 204 | struct rsnd_dai_stream *io, |
690602fc | 205 | struct rsnd_priv *priv) |
bff58ea4 KM |
206 | { |
207 | rsnd_mod_write(mod, CMD_CTRL, 0); | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
486b09c7 | 212 | static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, |
2c0fac19 | 213 | struct rsnd_dai_stream *io, |
486b09c7 KM |
214 | struct snd_soc_pcm_runtime *rtd) |
215 | { | |
486b09c7 | 216 | struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); |
985a4f6e | 217 | int is_play = rsnd_io_is_play(io); |
486b09c7 KM |
218 | int ret; |
219 | ||
220 | /* Volume */ | |
b65a7ccc | 221 | ret = rsnd_kctrl_new_m(mod, io, rtd, |
985a4f6e | 222 | is_play ? |
486b09c7 | 223 | "DVC Out Playback Volume" : "DVC In Capture Volume", |
170a2497 | 224 | rsnd_dvc_volume_update, |
ec14af91 | 225 | &dvc->volume, 0x00800000 - 1); |
486b09c7 KM |
226 | if (ret < 0) |
227 | return ret; | |
228 | ||
cd2b6574 | 229 | /* Mute */ |
b65a7ccc | 230 | ret = rsnd_kctrl_new_m(mod, io, rtd, |
985a4f6e | 231 | is_play ? |
cd2b6574 | 232 | "DVC Out Mute Switch" : "DVC In Mute Switch", |
170a2497 | 233 | rsnd_dvc_volume_update, |
ec14af91 | 234 | &dvc->mute, 1); |
cd2b6574 KM |
235 | if (ret < 0) |
236 | return ret; | |
237 | ||
3539cacf | 238 | /* Ramp */ |
b65a7ccc | 239 | ret = rsnd_kctrl_new_s(mod, io, rtd, |
985a4f6e | 240 | is_play ? |
3539cacf | 241 | "DVC Out Ramp Switch" : "DVC In Ramp Switch", |
170a2497 | 242 | rsnd_dvc_volume_update, |
3539cacf KM |
243 | &dvc->ren, 1); |
244 | if (ret < 0) | |
245 | return ret; | |
246 | ||
b65a7ccc | 247 | ret = rsnd_kctrl_new_e(mod, io, rtd, |
985a4f6e | 248 | is_play ? |
3539cacf KM |
249 | "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate", |
250 | &dvc->rup, | |
170a2497 | 251 | rsnd_dvc_volume_update, |
3539cacf KM |
252 | dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate)); |
253 | if (ret < 0) | |
254 | return ret; | |
255 | ||
b65a7ccc | 256 | ret = rsnd_kctrl_new_e(mod, io, rtd, |
985a4f6e | 257 | is_play ? |
3539cacf KM |
258 | "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate", |
259 | &dvc->rdown, | |
170a2497 | 260 | rsnd_dvc_volume_update, |
3539cacf KM |
261 | dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate)); |
262 | ||
263 | if (ret < 0) | |
264 | return ret; | |
265 | ||
486b09c7 KM |
266 | return 0; |
267 | } | |
268 | ||
9b99e9a7 KM |
269 | static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io, |
270 | struct rsnd_mod *mod) | |
72adc61f KM |
271 | { |
272 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | |
273 | ||
274 | return rsnd_dma_request_channel(rsnd_dvc_of_node(priv), | |
275 | mod, "tx"); | |
276 | } | |
277 | ||
bff58ea4 | 278 | static struct rsnd_mod_ops rsnd_dvc_ops = { |
8aefda50 | 279 | .name = DVC_NAME, |
72adc61f | 280 | .dma_req = rsnd_dvc_dma_req, |
d1f83d6e | 281 | .remove = rsnd_dvc_remove_gen2, |
bff58ea4 KM |
282 | .init = rsnd_dvc_init, |
283 | .quit = rsnd_dvc_quit, | |
284 | .start = rsnd_dvc_start, | |
285 | .stop = rsnd_dvc_stop, | |
286 | .pcm_new = rsnd_dvc_pcm_new, | |
287 | }; | |
288 | ||
289 | struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) | |
290 | { | |
291 | if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv))) | |
292 | id = 0; | |
293 | ||
294 | return &((struct rsnd_dvc *)(priv->dvc) + id)->mod; | |
295 | } | |
296 | ||
34cb6123 KM |
297 | static void rsnd_of_parse_dvc(struct platform_device *pdev, |
298 | const struct rsnd_of_data *of_data, | |
299 | struct rsnd_priv *priv) | |
300 | { | |
301 | struct device_node *node; | |
302 | struct rsnd_dvc_platform_info *dvc_info; | |
303 | struct rcar_snd_info *info = rsnd_priv_to_info(priv); | |
304 | struct device *dev = &pdev->dev; | |
305 | int nr; | |
306 | ||
307 | if (!of_data) | |
308 | return; | |
309 | ||
310 | node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc"); | |
311 | if (!node) | |
312 | return; | |
313 | ||
314 | nr = of_get_child_count(node); | |
315 | if (!nr) | |
316 | goto rsnd_of_parse_dvc_end; | |
317 | ||
318 | dvc_info = devm_kzalloc(dev, | |
319 | sizeof(struct rsnd_dvc_platform_info) * nr, | |
320 | GFP_KERNEL); | |
321 | if (!dvc_info) { | |
322 | dev_err(dev, "dvc info allocation error\n"); | |
323 | goto rsnd_of_parse_dvc_end; | |
324 | } | |
325 | ||
326 | info->dvc_info = dvc_info; | |
327 | info->dvc_info_nr = nr; | |
328 | ||
329 | rsnd_of_parse_dvc_end: | |
330 | of_node_put(node); | |
331 | } | |
332 | ||
bff58ea4 KM |
333 | int rsnd_dvc_probe(struct platform_device *pdev, |
334 | const struct rsnd_of_data *of_data, | |
335 | struct rsnd_priv *priv) | |
336 | { | |
337 | struct rcar_snd_info *info = rsnd_priv_to_info(priv); | |
338 | struct device *dev = rsnd_priv_to_dev(priv); | |
339 | struct rsnd_dvc *dvc; | |
340 | struct clk *clk; | |
341 | char name[RSND_DVC_NAME_SIZE]; | |
2f78dd7f | 342 | int i, nr, ret; |
bff58ea4 | 343 | |
34cb6123 KM |
344 | rsnd_of_parse_dvc(pdev, of_data, priv); |
345 | ||
bff58ea4 KM |
346 | nr = info->dvc_info_nr; |
347 | if (!nr) | |
348 | return 0; | |
349 | ||
350 | /* This driver doesn't support Gen1 at this point */ | |
351 | if (rsnd_is_gen1(priv)) { | |
352 | dev_warn(dev, "CMD is not supported on Gen1\n"); | |
353 | return -EINVAL; | |
354 | } | |
355 | ||
356 | dvc = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL); | |
357 | if (!dvc) { | |
358 | dev_err(dev, "CMD allocate failed\n"); | |
359 | return -ENOMEM; | |
360 | } | |
361 | ||
362 | priv->dvc_nr = nr; | |
363 | priv->dvc = dvc; | |
364 | ||
365 | for_each_rsnd_dvc(dvc, priv, i) { | |
8aefda50 KM |
366 | snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d", |
367 | DVC_NAME, i); | |
bff58ea4 KM |
368 | |
369 | clk = devm_clk_get(dev, name); | |
370 | if (IS_ERR(clk)) | |
371 | return PTR_ERR(clk); | |
372 | ||
373 | dvc->info = &info->dvc_info[i]; | |
bff58ea4 | 374 | |
2099bc8e | 375 | ret = rsnd_mod_init(priv, &dvc->mod, &rsnd_dvc_ops, |
85642952 | 376 | clk, RSND_MOD_DVC, i); |
2f78dd7f KM |
377 | if (ret) |
378 | return ret; | |
bff58ea4 KM |
379 | } |
380 | ||
381 | return 0; | |
382 | } | |
2f78dd7f KM |
383 | |
384 | void rsnd_dvc_remove(struct platform_device *pdev, | |
385 | struct rsnd_priv *priv) | |
386 | { | |
387 | struct rsnd_dvc *dvc; | |
388 | int i; | |
389 | ||
390 | for_each_rsnd_dvc(dvc, priv, i) { | |
391 | rsnd_mod_quit(&dvc->mod); | |
392 | } | |
393 | } |