Commit | Line | Data |
---|---|---|
1e0edd4d KM |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // | |
3 | // Renesas R-Car SRC support | |
4 | // | |
5 | // Copyright (C) 2013 Renesas Solutions Corp. | |
6 | // Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | |
2b627869 | 7 | |
63346d3d KM |
8 | /* |
9 | * You can use Synchronous Sampling Rate Convert (if no DVC) | |
10 | * | |
11 | * amixer set "SRC Out Rate" on | |
12 | * aplay xxx.wav & | |
13 | * amixer set "SRC Out Rate" 96000 // convert rate to 96000Hz | |
14 | * amixer set "SRC Out Rate" 22050 // convert rate to 22050Hz | |
15 | */ | |
16 | ||
2b627869 KM |
17 | /* |
18 | * you can enable below define if you don't need | |
19 | * SSI interrupt status debug message when debugging | |
1788a152 | 20 | * see rsnd_print_irq_status() |
2b627869 KM |
21 | * |
22 | * #define RSND_DEBUG_NO_IRQ_STATUS 1 | |
23 | */ | |
24 | ||
340d79a1 | 25 | #include <linux/of_irq.h> |
07539c1d KM |
26 | #include "rsnd.h" |
27 | ||
8aefda50 KM |
28 | #define SRC_NAME "src" |
29 | ||
cfcefe01 KM |
30 | /* SCU_SYSTEM_STATUS0/1 */ |
31 | #define OUF_SRC(id) ((1 << (id + 16)) | (1 << id)) | |
32 | ||
ba9c949f | 33 | struct rsnd_src { |
07539c1d | 34 | struct rsnd_mod mod; |
940e9479 | 35 | struct rsnd_mod *dma; |
43cb6954 KM |
36 | struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ |
37 | struct rsnd_kctrl_cfg_s sync; /* sync convert */ | |
adf6a681 | 38 | int irq; |
07539c1d KM |
39 | }; |
40 | ||
ba9c949f | 41 | #define RSND_SRC_NAME_SIZE 16 |
374a5281 | 42 | |
adf6a681 | 43 | #define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id) |
da599fd3 | 44 | #define rsnd_src_nr(priv) ((priv)->src_nr) |
ab2049f9 | 45 | #define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val) |
82e76ed3 | 46 | |
ba9c949f KM |
47 | #define rsnd_mod_to_src(_mod) \ |
48 | container_of((_mod), struct rsnd_src, mod) | |
39cf3c40 | 49 | |
ba9c949f | 50 | #define for_each_rsnd_src(pos, priv, i) \ |
39cf3c40 | 51 | for ((i) = 0; \ |
ba9c949f KM |
52 | ((i) < rsnd_src_nr(priv)) && \ |
53 | ((pos) = (struct rsnd_src *)(priv)->src + i); \ | |
39cf3c40 KM |
54 | i++) |
55 | ||
56 | ||
ef749400 KM |
57 | /* |
58 | * image of SRC (Sampling Rate Converter) | |
59 | * | |
60 | * 96kHz <-> +-----+ 48kHz +-----+ 48kHz +-------+ | |
61 | * 48kHz <-> | SRC | <------> | SSI | <-----> | codec | | |
62 | * 44.1kHz <-> +-----+ +-----+ +-------+ | |
63 | * ... | |
64 | * | |
65 | */ | |
374a5281 | 66 | |
98efeeae | 67 | static void rsnd_src_activation(struct rsnd_mod *mod) |
379febfd KM |
68 | { |
69 | rsnd_mod_write(mod, SRC_SWRSR, 0); | |
70 | rsnd_mod_write(mod, SRC_SWRSR, 1); | |
71 | } | |
72 | ||
475a361a KM |
73 | static void rsnd_src_halt(struct rsnd_mod *mod) |
74 | { | |
75 | rsnd_mod_write(mod, SRC_SRCIR, 1); | |
76 | rsnd_mod_write(mod, SRC_SWRSR, 0); | |
77 | } | |
78 | ||
9b99e9a7 KM |
79 | static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io, |
80 | struct rsnd_mod *mod) | |
72adc61f KM |
81 | { |
82 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | |
72adc61f KM |
83 | int is_play = rsnd_io_is_play(io); |
84 | ||
85 | return rsnd_dma_request_channel(rsnd_src_of_node(priv), | |
039f2ccc | 86 | SRC_NAME, mod, |
72adc61f KM |
87 | is_play ? "rx" : "tx"); |
88 | } | |
89 | ||
88c61cff | 90 | static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, |
1a9be9ee | 91 | struct rsnd_mod *mod) |
43cb6954 | 92 | { |
43cb6954 | 93 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); |
1a9be9ee | 94 | struct rsnd_src *src = rsnd_mod_to_src(mod); |
43cb6954 KM |
95 | u32 convert_rate; |
96 | ||
97 | if (!runtime) | |
98 | return 0; | |
99 | ||
ab2049f9 | 100 | if (!rsnd_src_sync_is_enabled(mod)) |
c2aaaa57 | 101 | return rsnd_io_converted_rate(io); |
43cb6954 KM |
102 | |
103 | convert_rate = src->sync.val; | |
104 | ||
105 | if (!convert_rate) | |
c2aaaa57 | 106 | convert_rate = rsnd_io_converted_rate(io); |
43cb6954 KM |
107 | |
108 | if (!convert_rate) | |
109 | convert_rate = runtime->rate; | |
110 | ||
111 | return convert_rate; | |
112 | } | |
113 | ||
cbf1494f KM |
114 | unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, |
115 | struct rsnd_dai_stream *io, | |
116 | int is_in) | |
1b7b08ef | 117 | { |
b1eac430 | 118 | struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io); |
cbf1494f | 119 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); |
b1eac430 | 120 | unsigned int rate = 0; |
cbf1494f KM |
121 | int is_play = rsnd_io_is_play(io); |
122 | ||
123 | /* | |
cbf1494f KM |
124 | * Playback |
125 | * runtime_rate -> [SRC] -> convert_rate | |
126 | * | |
127 | * Capture | |
128 | * convert_rate -> [SRC] -> runtime_rate | |
129 | */ | |
130 | ||
131 | if (is_play == is_in) | |
132 | return runtime->rate; | |
1b7b08ef | 133 | |
1a9be9ee KM |
134 | /* |
135 | * return convert rate if SRC is used, | |
136 | * otherwise, return runtime->rate as usual | |
137 | */ | |
138 | if (src_mod) | |
139 | rate = rsnd_src_convert_rate(io, src_mod); | |
1b7b08ef | 140 | |
1b7b08ef KM |
141 | if (!rate) |
142 | rate = runtime->rate; | |
143 | ||
144 | return rate; | |
145 | } | |
146 | ||
399706df | 147 | static const u32 bsdsr_table_pattern1[] = { |
7674bec4 KM |
148 | 0x01800000, /* 6 - 1/6 */ |
149 | 0x01000000, /* 6 - 1/4 */ | |
150 | 0x00c00000, /* 6 - 1/3 */ | |
151 | 0x00800000, /* 6 - 1/2 */ | |
152 | 0x00600000, /* 6 - 2/3 */ | |
153 | 0x00400000, /* 6 - 1 */ | |
154 | }; | |
155 | ||
399706df | 156 | static const u32 bsdsr_table_pattern2[] = { |
7674bec4 KM |
157 | 0x02400000, /* 6 - 1/6 */ |
158 | 0x01800000, /* 6 - 1/4 */ | |
159 | 0x01200000, /* 6 - 1/3 */ | |
160 | 0x00c00000, /* 6 - 1/2 */ | |
161 | 0x00900000, /* 6 - 2/3 */ | |
162 | 0x00600000, /* 6 - 1 */ | |
163 | }; | |
164 | ||
399706df | 165 | static const u32 bsisr_table[] = { |
7674bec4 KM |
166 | 0x00100060, /* 6 - 1/6 */ |
167 | 0x00100040, /* 6 - 1/4 */ | |
168 | 0x00100030, /* 6 - 1/3 */ | |
169 | 0x00100020, /* 6 - 1/2 */ | |
170 | 0x00100020, /* 6 - 2/3 */ | |
171 | 0x00100020, /* 6 - 1 */ | |
172 | }; | |
173 | ||
399706df | 174 | static const u32 chan288888[] = { |
7674bec4 KM |
175 | 0x00000006, /* 1 to 2 */ |
176 | 0x000001fe, /* 1 to 8 */ | |
177 | 0x000001fe, /* 1 to 8 */ | |
178 | 0x000001fe, /* 1 to 8 */ | |
179 | 0x000001fe, /* 1 to 8 */ | |
180 | 0x000001fe, /* 1 to 8 */ | |
181 | }; | |
182 | ||
399706df | 183 | static const u32 chan244888[] = { |
7674bec4 KM |
184 | 0x00000006, /* 1 to 2 */ |
185 | 0x0000001e, /* 1 to 4 */ | |
186 | 0x0000001e, /* 1 to 4 */ | |
187 | 0x000001fe, /* 1 to 8 */ | |
188 | 0x000001fe, /* 1 to 8 */ | |
189 | 0x000001fe, /* 1 to 8 */ | |
190 | }; | |
191 | ||
399706df | 192 | static const u32 chan222222[] = { |
7674bec4 KM |
193 | 0x00000006, /* 1 to 2 */ |
194 | 0x00000006, /* 1 to 2 */ | |
195 | 0x00000006, /* 1 to 2 */ | |
196 | 0x00000006, /* 1 to 2 */ | |
197 | 0x00000006, /* 1 to 2 */ | |
198 | 0x00000006, /* 1 to 2 */ | |
199 | }; | |
200 | ||
75916f65 KM |
201 | static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, |
202 | struct rsnd_mod *mod) | |
1b7b08ef | 203 | { |
75916f65 KM |
204 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
205 | struct device *dev = rsnd_priv_to_dev(priv); | |
206 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | |
90431eb4 | 207 | int is_play = rsnd_io_is_play(io); |
67923f77 | 208 | int use_src = 0; |
0102eed5 | 209 | u32 fin, fout; |
75916f65 KM |
210 | u32 ifscr, fsrate, adinr; |
211 | u32 cr, route; | |
90431eb4 | 212 | u32 i_busif, o_busif, tmp; |
7674bec4 KM |
213 | const u32 *bsdsr_table; |
214 | const u32 *chptn; | |
75916f65 | 215 | uint ratio; |
7674bec4 KM |
216 | int chan; |
217 | int idx; | |
1b7b08ef | 218 | |
75916f65 KM |
219 | if (!runtime) |
220 | return; | |
43cb6954 | 221 | |
0102eed5 KM |
222 | fin = rsnd_src_get_in_rate(priv, io); |
223 | fout = rsnd_src_get_out_rate(priv, io); | |
224 | ||
7674bec4 KM |
225 | chan = rsnd_runtime_channel_original(io); |
226 | ||
75916f65 | 227 | /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ |
0102eed5 | 228 | if (fin == fout) |
75916f65 | 229 | ratio = 0; |
0102eed5 KM |
230 | else if (fin > fout) |
231 | ratio = 100 * fin / fout; | |
75916f65 | 232 | else |
0102eed5 | 233 | ratio = 100 * fout / fin; |
1b7b08ef | 234 | |
75916f65 KM |
235 | if (ratio > 600) { |
236 | dev_err(dev, "FSO/FSI ratio error\n"); | |
237 | return; | |
238 | } | |
1b7b08ef | 239 | |
67923f77 KM |
240 | use_src = (fin != fout) | rsnd_src_sync_is_enabled(mod); |
241 | ||
75916f65 | 242 | /* |
b65cb7a5 | 243 | * SRC_ADINR |
75916f65 | 244 | */ |
7674bec4 | 245 | adinr = rsnd_get_adinr_bit(mod, io) | chan; |
1b7b08ef | 246 | |
75916f65 | 247 | /* |
b65cb7a5 | 248 | * SRC_IFSCR / SRC_IFSVR |
75916f65 KM |
249 | */ |
250 | ifscr = 0; | |
251 | fsrate = 0; | |
67923f77 | 252 | if (use_src) { |
93ca33c9 HY |
253 | u64 n; |
254 | ||
75916f65 | 255 | ifscr = 1; |
93ca33c9 HY |
256 | n = (u64)0x0400000 * fin; |
257 | do_div(n, fout); | |
258 | fsrate = n; | |
75916f65 | 259 | } |
cfcefe01 | 260 | |
75916f65 | 261 | /* |
b65cb7a5 | 262 | * SRC_SRCCR / SRC_ROUTE_MODE0 |
75916f65 KM |
263 | */ |
264 | cr = 0x00011110; | |
265 | route = 0x0; | |
67923f77 | 266 | if (use_src) { |
75916f65 | 267 | route = 0x1; |
3b7843ff | 268 | |
ab2049f9 | 269 | if (rsnd_src_sync_is_enabled(mod)) { |
75916f65 KM |
270 | cr |= 0x1; |
271 | route |= rsnd_io_is_play(io) ? | |
272 | (0x1 << 24) : (0x1 << 25); | |
273 | } | |
274 | } | |
43cb6954 | 275 | |
75916f65 KM |
276 | /* |
277 | * SRC_BSDSR / SRC_BSISR | |
7674bec4 KM |
278 | * |
279 | * see | |
280 | * Combination of Register Setting Related to | |
281 | * FSO/FSI Ratio and Channel, Latency | |
75916f65 KM |
282 | */ |
283 | switch (rsnd_mod_id(mod)) { | |
7674bec4 KM |
284 | case 0: |
285 | chptn = chan288888; | |
286 | bsdsr_table = bsdsr_table_pattern1; | |
287 | break; | |
288 | case 1: | |
289 | case 3: | |
290 | case 4: | |
291 | chptn = chan244888; | |
292 | bsdsr_table = bsdsr_table_pattern1; | |
293 | break; | |
294 | case 2: | |
295 | case 9: | |
296 | chptn = chan222222; | |
297 | bsdsr_table = bsdsr_table_pattern1; | |
298 | break; | |
75916f65 KM |
299 | case 5: |
300 | case 6: | |
301 | case 7: | |
302 | case 8: | |
7674bec4 KM |
303 | chptn = chan222222; |
304 | bsdsr_table = bsdsr_table_pattern2; | |
75916f65 KM |
305 | break; |
306 | default: | |
7674bec4 | 307 | goto convert_rate_err; |
75916f65 | 308 | } |
1b7b08ef | 309 | |
7674bec4 KM |
310 | /* |
311 | * E3 need to overwrite | |
312 | */ | |
0b8ef53e | 313 | if (rsnd_is_gen3_e3(priv)) |
7674bec4 KM |
314 | switch (rsnd_mod_id(mod)) { |
315 | case 0: | |
316 | case 4: | |
317 | chptn = chan222222; | |
318 | } | |
319 | ||
320 | for (idx = 0; idx < ARRAY_SIZE(chan222222); idx++) | |
321 | if (chptn[idx] & (1 << chan)) | |
322 | break; | |
323 | ||
324 | if (chan > 8 || | |
325 | idx >= ARRAY_SIZE(chan222222)) | |
326 | goto convert_rate_err; | |
327 | ||
90431eb4 KM |
328 | /* BUSIF_MODE */ |
329 | tmp = rsnd_get_busif_shift(io, mod); | |
330 | i_busif = ( is_play ? tmp : 0) | 1; | |
331 | o_busif = (!is_play ? tmp : 0) | 1; | |
332 | ||
0fbab951 KM |
333 | rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); |
334 | ||
75916f65 KM |
335 | rsnd_mod_write(mod, SRC_SRCIR, 1); /* initialize */ |
336 | rsnd_mod_write(mod, SRC_ADINR, adinr); | |
337 | rsnd_mod_write(mod, SRC_IFSCR, ifscr); | |
338 | rsnd_mod_write(mod, SRC_IFSVR, fsrate); | |
339 | rsnd_mod_write(mod, SRC_SRCCR, cr); | |
7674bec4 KM |
340 | rsnd_mod_write(mod, SRC_BSDSR, bsdsr_table[idx]); |
341 | rsnd_mod_write(mod, SRC_BSISR, bsisr_table[idx]); | |
75916f65 | 342 | rsnd_mod_write(mod, SRC_SRCIR, 0); /* cancel initialize */ |
1b7b08ef | 343 | |
90431eb4 KM |
344 | rsnd_mod_write(mod, SRC_I_BUSIF_MODE, i_busif); |
345 | rsnd_mod_write(mod, SRC_O_BUSIF_MODE, o_busif); | |
346 | ||
75916f65 | 347 | rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); |
1b7b08ef | 348 | |
0102eed5 | 349 | rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout); |
7674bec4 KM |
350 | |
351 | return; | |
352 | ||
353 | convert_rate_err: | |
354 | dev_err(dev, "unknown BSDSR/BSDIR settings\n"); | |
1b7b08ef KM |
355 | } |
356 | ||
b5b442ab KM |
357 | static int rsnd_src_irq(struct rsnd_mod *mod, |
358 | struct rsnd_dai_stream *io, | |
359 | struct rsnd_priv *priv, | |
360 | int enable) | |
cfcefe01 KM |
361 | { |
362 | struct rsnd_src *src = rsnd_mod_to_src(mod); | |
363 | u32 sys_int_val, int_val, sys_int_mask; | |
adf6a681 | 364 | int irq = src->irq; |
cfcefe01 KM |
365 | int id = rsnd_mod_id(mod); |
366 | ||
367 | sys_int_val = | |
368 | sys_int_mask = OUF_SRC(id); | |
369 | int_val = 0x3300; | |
370 | ||
371 | /* | |
372 | * IRQ is not supported on non-DT | |
373 | * see | |
75916f65 | 374 | * rsnd_src_probe_() |
cfcefe01 KM |
375 | */ |
376 | if ((irq <= 0) || !enable) { | |
377 | sys_int_val = 0; | |
378 | int_val = 0; | |
379 | } | |
380 | ||
1a1bf58a KM |
381 | /* |
382 | * WORKAROUND | |
383 | * | |
ab2049f9 | 384 | * ignore over flow error when rsnd_src_sync_is_enabled() |
1a1bf58a | 385 | */ |
ab2049f9 | 386 | if (rsnd_src_sync_is_enabled(mod)) |
1a1bf58a KM |
387 | sys_int_val = sys_int_val & 0xffff; |
388 | ||
cfcefe01 KM |
389 | rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val); |
390 | rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val); | |
391 | rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val); | |
b5b442ab KM |
392 | |
393 | return 0; | |
cfcefe01 KM |
394 | } |
395 | ||
8cc225f7 | 396 | static void rsnd_src_status_clear(struct rsnd_mod *mod) |
cfcefe01 KM |
397 | { |
398 | u32 val = OUF_SRC(rsnd_mod_id(mod)); | |
399 | ||
42b197e7 KM |
400 | rsnd_mod_write(mod, SCU_SYS_STATUS0, val); |
401 | rsnd_mod_write(mod, SCU_SYS_STATUS1, val); | |
cfcefe01 KM |
402 | } |
403 | ||
6a25c8da | 404 | static bool rsnd_src_error_occurred(struct rsnd_mod *mod) |
cfcefe01 | 405 | { |
2b627869 KM |
406 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
407 | struct device *dev = rsnd_priv_to_dev(priv); | |
1a1bf58a | 408 | u32 val0, val1; |
2b627869 | 409 | u32 status0, status1; |
cfcefe01 KM |
410 | bool ret = false; |
411 | ||
1a1bf58a KM |
412 | val0 = val1 = OUF_SRC(rsnd_mod_id(mod)); |
413 | ||
414 | /* | |
415 | * WORKAROUND | |
416 | * | |
ab2049f9 | 417 | * ignore over flow error when rsnd_src_sync_is_enabled() |
1a1bf58a | 418 | */ |
ab2049f9 | 419 | if (rsnd_src_sync_is_enabled(mod)) |
1a1bf58a KM |
420 | val0 = val0 & 0xffff; |
421 | ||
2b627869 KM |
422 | status0 = rsnd_mod_read(mod, SCU_SYS_STATUS0); |
423 | status1 = rsnd_mod_read(mod, SCU_SYS_STATUS1); | |
424 | if ((status0 & val0) || (status1 & val1)) { | |
1788a152 KM |
425 | rsnd_print_irq_status(dev, "%s err status : 0x%08x, 0x%08x\n", |
426 | rsnd_mod_name(mod), status0, status1); | |
2b627869 | 427 | |
cfcefe01 | 428 | ret = true; |
2b627869 | 429 | } |
cfcefe01 | 430 | |
cfcefe01 KM |
431 | return ret; |
432 | } | |
433 | ||
75916f65 KM |
434 | static int rsnd_src_start(struct rsnd_mod *mod, |
435 | struct rsnd_dai_stream *io, | |
436 | struct rsnd_priv *priv) | |
cfcefe01 | 437 | { |
1a1bf58a KM |
438 | u32 val; |
439 | ||
440 | /* | |
441 | * WORKAROUND | |
442 | * | |
443 | * Enable SRC output if you want to use sync convert together with DVC | |
444 | */ | |
ab2049f9 | 445 | val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ? |
1a1bf58a | 446 | 0x01 : 0x11; |
cfcefe01 KM |
447 | |
448 | rsnd_mod_write(mod, SRC_CTRL, val); | |
449 | ||
cfcefe01 KM |
450 | return 0; |
451 | } | |
452 | ||
75916f65 KM |
453 | static int rsnd_src_stop(struct rsnd_mod *mod, |
454 | struct rsnd_dai_stream *io, | |
455 | struct rsnd_priv *priv) | |
cfcefe01 | 456 | { |
31739a68 | 457 | rsnd_mod_write(mod, SRC_CTRL, 0); |
cfcefe01 | 458 | |
75916f65 KM |
459 | return 0; |
460 | } | |
461 | ||
462 | static int rsnd_src_init(struct rsnd_mod *mod, | |
463 | struct rsnd_dai_stream *io, | |
464 | struct rsnd_priv *priv) | |
465 | { | |
466 | struct rsnd_src *src = rsnd_mod_to_src(mod); | |
376be51c | 467 | int ret; |
cfcefe01 | 468 | |
ef30da1c KM |
469 | /* reset sync convert_rate */ |
470 | src->sync.val = 0; | |
471 | ||
376be51c JJ |
472 | ret = rsnd_mod_power_on(mod); |
473 | if (ret < 0) | |
474 | return ret; | |
75916f65 | 475 | |
98efeeae | 476 | rsnd_src_activation(mod); |
75916f65 KM |
477 | |
478 | rsnd_src_set_convert_rate(io, mod); | |
479 | ||
8cc225f7 | 480 | rsnd_src_status_clear(mod); |
75916f65 | 481 | |
75916f65 | 482 | return 0; |
cfcefe01 KM |
483 | } |
484 | ||
75916f65 KM |
485 | static int rsnd_src_quit(struct rsnd_mod *mod, |
486 | struct rsnd_dai_stream *io, | |
487 | struct rsnd_priv *priv) | |
b761bf27 | 488 | { |
75916f65 | 489 | struct rsnd_src *src = rsnd_mod_to_src(mod); |
75916f65 | 490 | |
475a361a KM |
491 | rsnd_src_halt(mod); |
492 | ||
75916f65 KM |
493 | rsnd_mod_power_off(mod); |
494 | ||
75916f65 KM |
495 | /* reset sync convert_rate */ |
496 | src->sync.val = 0; | |
497 | ||
498 | return 0; | |
b761bf27 KM |
499 | } |
500 | ||
75916f65 KM |
501 | static void __rsnd_src_interrupt(struct rsnd_mod *mod, |
502 | struct rsnd_dai_stream *io) | |
cfcefe01 | 503 | { |
02299d98 | 504 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
6a25c8da | 505 | bool stop = false; |
02299d98 KM |
506 | |
507 | spin_lock(&priv->lock); | |
cfcefe01 | 508 | |
02299d98 | 509 | /* ignore all cases if not working */ |
d5bbe7de | 510 | if (!rsnd_io_is_working(io)) |
75916f65 | 511 | goto rsnd_src_interrupt_out; |
cfcefe01 | 512 | |
6a25c8da KM |
513 | if (rsnd_src_error_occurred(mod)) |
514 | stop = true; | |
88c61cff | 515 | |
8cc225f7 | 516 | rsnd_src_status_clear(mod); |
75916f65 | 517 | rsnd_src_interrupt_out: |
8cc225f7 | 518 | |
02299d98 | 519 | spin_unlock(&priv->lock); |
6a25c8da KM |
520 | |
521 | if (stop) | |
522 | snd_pcm_stop_xrun(io->substream); | |
88c61cff KM |
523 | } |
524 | ||
75916f65 | 525 | static irqreturn_t rsnd_src_interrupt(int irq, void *data) |
88c61cff KM |
526 | { |
527 | struct rsnd_mod *mod = data; | |
528 | ||
75916f65 | 529 | rsnd_mod_interrupt(mod, __rsnd_src_interrupt); |
cfcefe01 KM |
530 | |
531 | return IRQ_HANDLED; | |
532 | } | |
533 | ||
75916f65 KM |
534 | static int rsnd_src_probe_(struct rsnd_mod *mod, |
535 | struct rsnd_dai_stream *io, | |
536 | struct rsnd_priv *priv) | |
76c6fb5c | 537 | { |
ba9c949f | 538 | struct rsnd_src *src = rsnd_mod_to_src(mod); |
76c6fb5c | 539 | struct device *dev = rsnd_priv_to_dev(priv); |
adf6a681 | 540 | int irq = src->irq; |
76c6fb5c | 541 | int ret; |
76c6fb5c | 542 | |
cfcefe01 KM |
543 | if (irq > 0) { |
544 | /* | |
545 | * IRQ is not supported on non-DT | |
546 | * see | |
b5b442ab | 547 | * rsnd_src_irq() |
cfcefe01 KM |
548 | */ |
549 | ret = devm_request_irq(dev, irq, | |
75916f65 | 550 | rsnd_src_interrupt, |
cfcefe01 KM |
551 | IRQF_SHARED, |
552 | dev_name(dev), mod); | |
553 | if (ret) | |
b543b52a | 554 | return ret; |
cfcefe01 KM |
555 | } |
556 | ||
b99305d2 | 557 | ret = rsnd_dma_attach(io, mod, &src->dma); |
8aefda50 | 558 | |
76c6fb5c KM |
559 | return ret; |
560 | } | |
561 | ||
75916f65 | 562 | static int rsnd_src_pcm_new(struct rsnd_mod *mod, |
2c0fac19 | 563 | struct rsnd_dai_stream *io, |
43cb6954 KM |
564 | struct snd_soc_pcm_runtime *rtd) |
565 | { | |
43cb6954 KM |
566 | struct rsnd_src *src = rsnd_mod_to_src(mod); |
567 | int ret; | |
568 | ||
569 | /* | |
570 | * enable SRC sync convert if possible | |
571 | */ | |
572 | ||
7115cb91 | 573 | /* |
61a219fe KM |
574 | * It can't use SRC Synchronous convert |
575 | * when Capture if it uses CMD | |
7115cb91 | 576 | */ |
61a219fe | 577 | if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io)) |
7115cb91 KM |
578 | return 0; |
579 | ||
43cb6954 KM |
580 | /* |
581 | * enable sync convert | |
582 | */ | |
b65a7ccc | 583 | ret = rsnd_kctrl_new_s(mod, io, rtd, |
43cb6954 KM |
584 | rsnd_io_is_play(io) ? |
585 | "SRC Out Rate Switch" : | |
586 | "SRC In Rate Switch", | |
f0b04d8b | 587 | rsnd_kctrl_accept_anytime, |
75916f65 | 588 | rsnd_src_set_convert_rate, |
43cb6954 KM |
589 | &src->sen, 1); |
590 | if (ret < 0) | |
591 | return ret; | |
592 | ||
b65a7ccc | 593 | ret = rsnd_kctrl_new_s(mod, io, rtd, |
43cb6954 KM |
594 | rsnd_io_is_play(io) ? |
595 | "SRC Out Rate" : | |
596 | "SRC In Rate", | |
f0b04d8b | 597 | rsnd_kctrl_accept_runtime, |
75916f65 | 598 | rsnd_src_set_convert_rate, |
43cb6954 KM |
599 | &src->sync, 192000); |
600 | ||
601 | return ret; | |
602 | } | |
603 | ||
1f9c82b5 KM |
604 | #ifdef CONFIG_DEBUG_FS |
605 | static void rsnd_src_debug_info(struct seq_file *m, | |
606 | struct rsnd_dai_stream *io, | |
607 | struct rsnd_mod *mod) | |
608 | { | |
6e4e5432 | 609 | rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, |
1f9c82b5 KM |
610 | rsnd_mod_id(mod) * 0x20, 0x20); |
611 | seq_puts(m, "\n"); | |
6e4e5432 | 612 | rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, |
1f9c82b5 KM |
613 | 0x1c0, 0x20); |
614 | seq_puts(m, "\n"); | |
6e4e5432 | 615 | rsnd_debugfs_mod_reg_show(m, mod, RSND_BASE_SCU, |
1f9c82b5 KM |
616 | 0x200 + rsnd_mod_id(mod) * 0x40, 0x40); |
617 | } | |
618 | #define DEBUG_INFO .debug_info = rsnd_src_debug_info | |
619 | #else | |
620 | #define DEBUG_INFO | |
621 | #endif | |
622 | ||
75916f65 | 623 | static struct rsnd_mod_ops rsnd_src_ops = { |
7e7fe06d KM |
624 | .name = SRC_NAME, |
625 | .dma_req = rsnd_src_dma_req, | |
626 | .probe = rsnd_src_probe_, | |
627 | .init = rsnd_src_init, | |
628 | .quit = rsnd_src_quit, | |
629 | .start = rsnd_src_start, | |
630 | .stop = rsnd_src_stop, | |
631 | .irq = rsnd_src_irq, | |
7e7fe06d KM |
632 | .pcm_new = rsnd_src_pcm_new, |
633 | .get_status = rsnd_mod_get_status, | |
1f9c82b5 | 634 | DEBUG_INFO |
629509c5 KM |
635 | }; |
636 | ||
ba9c949f | 637 | struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) |
07539c1d | 638 | { |
ba9c949f | 639 | if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv))) |
8b14719b | 640 | id = 0; |
07539c1d | 641 | |
adf6a681 | 642 | return rsnd_mod_get(rsnd_src_get(priv, id)); |
90e8e50f KM |
643 | } |
644 | ||
2ea6b074 | 645 | int rsnd_src_probe(struct rsnd_priv *priv) |
07539c1d | 646 | { |
adf6a681 KM |
647 | struct device_node *node; |
648 | struct device_node *np; | |
07539c1d | 649 | struct device *dev = rsnd_priv_to_dev(priv); |
ba9c949f | 650 | struct rsnd_src *src; |
ef749400 | 651 | struct clk *clk; |
ba9c949f | 652 | char name[RSND_SRC_NAME_SIZE]; |
2f78dd7f | 653 | int i, nr, ret; |
07539c1d | 654 | |
adf6a681 KM |
655 | node = rsnd_src_of_node(priv); |
656 | if (!node) | |
657 | return 0; /* not used is not error */ | |
90e8e50f | 658 | |
c413983e | 659 | nr = rsnd_node_count(priv, node, SRC_NAME); |
adf6a681 KM |
660 | if (!nr) { |
661 | ret = -EINVAL; | |
662 | goto rsnd_src_probe_done; | |
663 | } | |
389933d9 | 664 | |
a86854d0 | 665 | src = devm_kcalloc(dev, nr, sizeof(*src), GFP_KERNEL); |
adf6a681 KM |
666 | if (!src) { |
667 | ret = -ENOMEM; | |
668 | goto rsnd_src_probe_done; | |
669 | } | |
07539c1d | 670 | |
ba9c949f KM |
671 | priv->src_nr = nr; |
672 | priv->src = src; | |
07539c1d | 673 | |
adf6a681 KM |
674 | i = 0; |
675 | for_each_child_of_node(node, np) { | |
de196515 SS |
676 | if (!of_device_is_available(np)) |
677 | goto skip; | |
678 | ||
d09a7db4 KM |
679 | i = rsnd_node_fixed_index(dev, np, SRC_NAME, i); |
680 | if (i < 0) { | |
681 | ret = -EINVAL; | |
682 | of_node_put(np); | |
683 | goto rsnd_src_probe_done; | |
684 | } | |
c413983e | 685 | |
adf6a681 KM |
686 | src = rsnd_src_get(priv, i); |
687 | ||
8aefda50 KM |
688 | snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d", |
689 | SRC_NAME, i); | |
ef749400 | 690 | |
adf6a681 KM |
691 | src->irq = irq_of_parse_and_map(np, 0); |
692 | if (!src->irq) { | |
693 | ret = -EINVAL; | |
53ba2aa3 | 694 | of_node_put(np); |
adf6a681 KM |
695 | goto rsnd_src_probe_done; |
696 | } | |
ef749400 | 697 | |
adf6a681 KM |
698 | clk = devm_clk_get(dev, name); |
699 | if (IS_ERR(clk)) { | |
700 | ret = PTR_ERR(clk); | |
53ba2aa3 | 701 | of_node_put(np); |
adf6a681 KM |
702 | goto rsnd_src_probe_done; |
703 | } | |
07539c1d | 704 | |
e8e7b7bd | 705 | ret = rsnd_mod_init(priv, rsnd_mod_get(src), |
7e7fe06d | 706 | &rsnd_src_ops, clk, RSND_MOD_SRC, i); |
53ba2aa3 JL |
707 | if (ret) { |
708 | of_node_put(np); | |
adf6a681 | 709 | goto rsnd_src_probe_done; |
53ba2aa3 | 710 | } |
adf6a681 | 711 | |
de196515 | 712 | skip: |
adf6a681 | 713 | i++; |
374a5281 | 714 | } |
07539c1d | 715 | |
adf6a681 KM |
716 | ret = 0; |
717 | ||
718 | rsnd_src_probe_done: | |
719 | of_node_put(node); | |
720 | ||
721 | return ret; | |
07539c1d | 722 | } |
2f78dd7f | 723 | |
2ea6b074 | 724 | void rsnd_src_remove(struct rsnd_priv *priv) |
2f78dd7f KM |
725 | { |
726 | struct rsnd_src *src; | |
727 | int i; | |
728 | ||
729 | for_each_rsnd_src(src, priv, i) { | |
b76e218a | 730 | rsnd_mod_quit(rsnd_mod_get(src)); |
2f78dd7f KM |
731 | } |
732 | } |