diff options
Diffstat (limited to 'sound/soc/sh/rcar/scu.c')
-rw-r--r-- | sound/soc/sh/rcar/scu.c | 384 |
1 files changed, 0 insertions, 384 deletions
diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c deleted file mode 100644 index 9bb08bb1d455..000000000000 --- a/sound/soc/sh/rcar/scu.c +++ /dev/null | |||
@@ -1,384 +0,0 @@ | |||
1 | /* | ||
2 | * Renesas R-Car SCU support | ||
3 | * | ||
4 | * Copyright (C) 2013 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 | struct rsnd_scu { | ||
14 | struct rsnd_scu_platform_info *info; /* rcar_snd.h */ | ||
15 | struct rsnd_mod mod; | ||
16 | struct clk *clk; | ||
17 | }; | ||
18 | |||
19 | #define rsnd_scu_mode_flags(p) ((p)->info->flags) | ||
20 | #define rsnd_scu_convert_rate(p) ((p)->info->convert_rate) | ||
21 | |||
22 | #define RSND_SCU_NAME_SIZE 16 | ||
23 | |||
24 | /* | ||
25 | * ADINR | ||
26 | */ | ||
27 | #define OTBL_24 (0 << 16) | ||
28 | #define OTBL_22 (2 << 16) | ||
29 | #define OTBL_20 (4 << 16) | ||
30 | #define OTBL_18 (6 << 16) | ||
31 | #define OTBL_16 (8 << 16) | ||
32 | |||
33 | /* | ||
34 | * image of SRC (Sampling Rate Converter) | ||
35 | * | ||
36 | * 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+ | ||
37 | * 48kHz <-> | SRC | <------> | SSI | <-----> | codec | | ||
38 | * 44.1kHz <-> +-----+ +-----+ +-------+ | ||
39 | * ... | ||
40 | * | ||
41 | */ | ||
42 | |||
43 | #define rsnd_mod_to_scu(_mod) \ | ||
44 | container_of((_mod), struct rsnd_scu, mod) | ||
45 | |||
46 | #define for_each_rsnd_scu(pos, priv, i) \ | ||
47 | for ((i) = 0; \ | ||
48 | ((i) < rsnd_scu_nr(priv)) && \ | ||
49 | ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ | ||
50 | i++) | ||
51 | |||
52 | /* Gen1 only */ | ||
53 | static int rsnd_src_set_route_if_gen1(struct rsnd_priv *priv, | ||
54 | struct rsnd_mod *mod, | ||
55 | struct rsnd_dai *rdai, | ||
56 | struct rsnd_dai_stream *io) | ||
57 | { | ||
58 | struct scu_route_config { | ||
59 | u32 mask; | ||
60 | int shift; | ||
61 | } routes[] = { | ||
62 | { 0xF, 0, }, /* 0 */ | ||
63 | { 0xF, 4, }, /* 1 */ | ||
64 | { 0xF, 8, }, /* 2 */ | ||
65 | { 0x7, 12, }, /* 3 */ | ||
66 | { 0x7, 16, }, /* 4 */ | ||
67 | { 0x7, 20, }, /* 5 */ | ||
68 | { 0x7, 24, }, /* 6 */ | ||
69 | { 0x3, 28, }, /* 7 */ | ||
70 | { 0x3, 30, }, /* 8 */ | ||
71 | }; | ||
72 | struct rsnd_scu *scu = rsnd_mod_to_scu(mod); | ||
73 | u32 mask; | ||
74 | u32 val; | ||
75 | int shift; | ||
76 | int id; | ||
77 | |||
78 | /* | ||
79 | * Gen1 only | ||
80 | */ | ||
81 | if (!rsnd_is_gen1(priv)) | ||
82 | return 0; | ||
83 | |||
84 | id = rsnd_mod_id(mod); | ||
85 | if (id < 0 || id >= ARRAY_SIZE(routes)) | ||
86 | return -EIO; | ||
87 | |||
88 | /* | ||
89 | * SRC_ROUTE_SELECT | ||
90 | */ | ||
91 | val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; | ||
92 | val = val << routes[id].shift; | ||
93 | mask = routes[id].mask << routes[id].shift; | ||
94 | |||
95 | rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); | ||
96 | |||
97 | /* | ||
98 | * SRC_TIMING_SELECT | ||
99 | */ | ||
100 | shift = (id % 4) * 8; | ||
101 | mask = 0x1F << shift; | ||
102 | |||
103 | /* | ||
104 | * ADG is used as source clock if SRC was used, | ||
105 | * then, SSI WS is used as destination clock. | ||
106 | * SSI WS is used as source clock if SRC is not used | ||
107 | * (when playback, source/destination become reverse when capture) | ||
108 | */ | ||
109 | if (rsnd_scu_convert_rate(scu)) /* use ADG */ | ||
110 | val = 0; | ||
111 | else if (8 == id) /* use SSI WS, but SRU8 is special */ | ||
112 | val = id << shift; | ||
113 | else /* use SSI WS */ | ||
114 | val = (id + 1) << shift; | ||
115 | |||
116 | switch (id / 4) { | ||
117 | case 0: | ||
118 | rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); | ||
119 | break; | ||
120 | case 1: | ||
121 | rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); | ||
122 | break; | ||
123 | case 2: | ||
124 | rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); | ||
125 | break; | ||
126 | } | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | unsigned int rsnd_scu_get_ssi_rate(struct rsnd_priv *priv, | ||
132 | struct rsnd_mod *ssi_mod, | ||
133 | struct snd_pcm_runtime *runtime) | ||
134 | { | ||
135 | struct rsnd_scu *scu; | ||
136 | unsigned int rate; | ||
137 | |||
138 | /* this function is assuming SSI id = SCU id here */ | ||
139 | scu = rsnd_mod_to_scu(rsnd_scu_mod_get(priv, rsnd_mod_id(ssi_mod))); | ||
140 | |||
141 | /* | ||
142 | * return convert rate if SRC is used, | ||
143 | * otherwise, return runtime->rate as usual | ||
144 | */ | ||
145 | rate = rsnd_scu_convert_rate(scu); | ||
146 | if (!rate) | ||
147 | rate = runtime->rate; | ||
148 | |||
149 | return rate; | ||
150 | } | ||
151 | |||
152 | static int rsnd_scu_convert_rate_ctrl(struct rsnd_priv *priv, | ||
153 | struct rsnd_mod *mod, | ||
154 | struct rsnd_dai *rdai, | ||
155 | struct rsnd_dai_stream *io) | ||
156 | { | ||
157 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | ||
158 | struct rsnd_scu *scu = rsnd_mod_to_scu(mod); | ||
159 | u32 convert_rate = rsnd_scu_convert_rate(scu); | ||
160 | u32 adinr = runtime->channels; | ||
161 | |||
162 | /* set/clear soft reset */ | ||
163 | rsnd_mod_write(mod, SRC_SWRSR, 0); | ||
164 | rsnd_mod_write(mod, SRC_SWRSR, 1); | ||
165 | |||
166 | /* Initialize the operation of the SRC internal circuits */ | ||
167 | rsnd_mod_write(mod, SRC_SRCIR, 1); | ||
168 | |||
169 | /* Set channel number and output bit length */ | ||
170 | switch (runtime->sample_bits) { | ||
171 | case 16: | ||
172 | adinr |= OTBL_16; | ||
173 | break; | ||
174 | case 32: | ||
175 | adinr |= OTBL_24; | ||
176 | break; | ||
177 | default: | ||
178 | return -EIO; | ||
179 | } | ||
180 | rsnd_mod_write(mod, SRC_ADINR, adinr); | ||
181 | |||
182 | if (convert_rate) { | ||
183 | u32 fsrate = 0x0400000 / convert_rate * runtime->rate; | ||
184 | int ret; | ||
185 | |||
186 | /* Enable the initial value of IFS */ | ||
187 | rsnd_mod_write(mod, SRC_IFSCR, 1); | ||
188 | |||
189 | /* Set initial value of IFS */ | ||
190 | rsnd_mod_write(mod, SRC_IFSVR, fsrate); | ||
191 | |||
192 | /* Select SRC mode (fixed value) */ | ||
193 | rsnd_mod_write(mod, SRC_SRCCR, 0x00010110); | ||
194 | |||
195 | /* Set the restriction value of the FS ratio (98%) */ | ||
196 | rsnd_mod_write(mod, SRC_MNFSR, fsrate / 100 * 98); | ||
197 | |||
198 | if (rsnd_is_gen1(priv)) { | ||
199 | /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */ | ||
200 | } | ||
201 | |||
202 | /* set convert clock */ | ||
203 | ret = rsnd_adg_set_convert_clk(priv, mod, | ||
204 | runtime->rate, | ||
205 | convert_rate); | ||
206 | if (ret < 0) | ||
207 | return ret; | ||
208 | } | ||
209 | |||
210 | /* Cancel the initialization and operate the SRC function */ | ||
211 | rsnd_mod_write(mod, SRC_SRCIR, 0); | ||
212 | |||
213 | /* use DMA transfer */ | ||
214 | rsnd_mod_write(mod, BUSIF_MODE, 1); | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static int rsnd_scu_transfer_start(struct rsnd_priv *priv, | ||
220 | struct rsnd_mod *mod, | ||
221 | struct rsnd_dai *rdai, | ||
222 | struct rsnd_dai_stream *io) | ||
223 | { | ||
224 | struct rsnd_scu *scu = rsnd_mod_to_scu(mod); | ||
225 | int id = rsnd_mod_id(mod); | ||
226 | u32 val; | ||
227 | |||
228 | if (rsnd_is_gen1(priv)) { | ||
229 | val = (1 << id); | ||
230 | rsnd_mod_bset(mod, SRC_ROUTE_CTRL, val, val); | ||
231 | } | ||
232 | |||
233 | if (rsnd_scu_convert_rate(scu)) | ||
234 | rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static int rsnd_scu_transfer_stop(struct rsnd_priv *priv, | ||
240 | struct rsnd_mod *mod, | ||
241 | struct rsnd_dai *rdai, | ||
242 | struct rsnd_dai_stream *io) | ||
243 | { | ||
244 | struct rsnd_scu *scu = rsnd_mod_to_scu(mod); | ||
245 | int id = rsnd_mod_id(mod); | ||
246 | u32 mask; | ||
247 | |||
248 | if (rsnd_is_gen1(priv)) { | ||
249 | mask = (1 << id); | ||
250 | rsnd_mod_bset(mod, SRC_ROUTE_CTRL, mask, 0); | ||
251 | } | ||
252 | |||
253 | if (rsnd_scu_convert_rate(scu)) | ||
254 | rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | bool rsnd_scu_hpbif_is_enable(struct rsnd_mod *mod) | ||
260 | { | ||
261 | struct rsnd_scu *scu = rsnd_mod_to_scu(mod); | ||
262 | u32 flags = rsnd_scu_mode_flags(scu); | ||
263 | |||
264 | return !!(flags & RSND_SCU_USE_HPBIF); | ||
265 | } | ||
266 | |||
267 | static int rsnd_scu_start(struct rsnd_mod *mod, | ||
268 | struct rsnd_dai *rdai, | ||
269 | struct rsnd_dai_stream *io) | ||
270 | { | ||
271 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
272 | struct rsnd_scu *scu = rsnd_mod_to_scu(mod); | ||
273 | struct device *dev = rsnd_priv_to_dev(priv); | ||
274 | int ret; | ||
275 | |||
276 | /* | ||
277 | * SCU will be used if it has RSND_SCU_USE_HPBIF flags | ||
278 | */ | ||
279 | if (!rsnd_scu_hpbif_is_enable(mod)) { | ||
280 | /* it use PIO transter */ | ||
281 | dev_dbg(dev, "%s%d is not used\n", | ||
282 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
283 | |||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | clk_enable(scu->clk); | ||
288 | |||
289 | /* it use DMA transter */ | ||
290 | |||
291 | ret = rsnd_src_set_route_if_gen1(priv, mod, rdai, io); | ||
292 | if (ret < 0) | ||
293 | return ret; | ||
294 | |||
295 | ret = rsnd_scu_convert_rate_ctrl(priv, mod, rdai, io); | ||
296 | if (ret < 0) | ||
297 | return ret; | ||
298 | |||
299 | ret = rsnd_scu_transfer_start(priv, mod, rdai, io); | ||
300 | if (ret < 0) | ||
301 | return ret; | ||
302 | |||
303 | dev_dbg(dev, "%s%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
304 | |||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | static int rsnd_scu_stop(struct rsnd_mod *mod, | ||
309 | struct rsnd_dai *rdai, | ||
310 | struct rsnd_dai_stream *io) | ||
311 | { | ||
312 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
313 | struct rsnd_scu *scu = rsnd_mod_to_scu(mod); | ||
314 | |||
315 | if (!rsnd_scu_hpbif_is_enable(mod)) | ||
316 | return 0; | ||
317 | |||
318 | rsnd_scu_transfer_stop(priv, mod, rdai, io); | ||
319 | |||
320 | clk_disable(scu->clk); | ||
321 | |||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | static struct rsnd_mod_ops rsnd_scu_ops = { | ||
326 | .name = "scu", | ||
327 | .start = rsnd_scu_start, | ||
328 | .stop = rsnd_scu_stop, | ||
329 | }; | ||
330 | |||
331 | struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id) | ||
332 | { | ||
333 | if (WARN_ON(id < 0 || id >= rsnd_scu_nr(priv))) | ||
334 | id = 0; | ||
335 | |||
336 | return &((struct rsnd_scu *)(priv->scu) + id)->mod; | ||
337 | } | ||
338 | |||
339 | int rsnd_scu_probe(struct platform_device *pdev, | ||
340 | struct rcar_snd_info *info, | ||
341 | struct rsnd_priv *priv) | ||
342 | { | ||
343 | struct device *dev = rsnd_priv_to_dev(priv); | ||
344 | struct rsnd_scu *scu; | ||
345 | struct clk *clk; | ||
346 | char name[RSND_SCU_NAME_SIZE]; | ||
347 | int i, nr; | ||
348 | |||
349 | /* | ||
350 | * init SCU | ||
351 | */ | ||
352 | nr = info->scu_info_nr; | ||
353 | scu = devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL); | ||
354 | if (!scu) { | ||
355 | dev_err(dev, "SCU allocate failed\n"); | ||
356 | return -ENOMEM; | ||
357 | } | ||
358 | |||
359 | priv->scu_nr = nr; | ||
360 | priv->scu = scu; | ||
361 | |||
362 | for_each_rsnd_scu(scu, priv, i) { | ||
363 | snprintf(name, RSND_SCU_NAME_SIZE, "scu.%d", i); | ||
364 | |||
365 | clk = devm_clk_get(dev, name); | ||
366 | if (IS_ERR(clk)) | ||
367 | return PTR_ERR(clk); | ||
368 | |||
369 | rsnd_mod_init(priv, &scu->mod, | ||
370 | &rsnd_scu_ops, i); | ||
371 | scu->info = &info->scu_info[i]; | ||
372 | scu->clk = clk; | ||
373 | |||
374 | dev_dbg(dev, "SCU%d probed\n", i); | ||
375 | } | ||
376 | dev_dbg(dev, "scu probed\n"); | ||
377 | |||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | void rsnd_scu_remove(struct platform_device *pdev, | ||
382 | struct rsnd_priv *priv) | ||
383 | { | ||
384 | } | ||