Commit | Line | Data |
---|---|---|
ae5c3223 KM |
1 | /* |
2 | * Renesas R-Car SSIU/SSI support | |
3 | * | |
4 | * Copyright (C) 2013 Renesas Solutions Corp. | |
5 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | |
6 | * | |
7 | * Based on fsi.c | |
8 | * Kuninori Morimoto <morimoto.kuninori@renesas.com> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | */ | |
14 | #include <linux/delay.h> | |
15 | #include "rsnd.h" | |
16 | #define RSND_SSI_NAME_SIZE 16 | |
17 | ||
18 | /* | |
19 | * SSICR | |
20 | */ | |
21 | #define FORCE (1 << 31) /* Fixed */ | |
849fc82a | 22 | #define DMEN (1 << 28) /* DMA Enable */ |
ae5c3223 KM |
23 | #define UIEN (1 << 27) /* Underflow Interrupt Enable */ |
24 | #define OIEN (1 << 26) /* Overflow Interrupt Enable */ | |
25 | #define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ | |
26 | #define DIEN (1 << 24) /* Data Interrupt Enable */ | |
186fadc1 KM |
27 | #define CHNL_4 (1 << 22) /* Channels */ |
28 | #define CHNL_6 (2 << 22) /* Channels */ | |
29 | #define CHNL_8 (3 << 22) /* Channels */ | |
ae5c3223 KM |
30 | #define DWL_8 (0 << 19) /* Data Word Length */ |
31 | #define DWL_16 (1 << 19) /* Data Word Length */ | |
32 | #define DWL_18 (2 << 19) /* Data Word Length */ | |
33 | #define DWL_20 (3 << 19) /* Data Word Length */ | |
34 | #define DWL_22 (4 << 19) /* Data Word Length */ | |
35 | #define DWL_24 (5 << 19) /* Data Word Length */ | |
36 | #define DWL_32 (6 << 19) /* Data Word Length */ | |
37 | ||
38 | #define SWL_32 (3 << 16) /* R/W System Word Length */ | |
39 | #define SCKD (1 << 15) /* Serial Bit Clock Direction */ | |
40 | #define SWSD (1 << 14) /* Serial WS Direction */ | |
41 | #define SCKP (1 << 13) /* Serial Bit Clock Polarity */ | |
42 | #define SWSP (1 << 12) /* Serial WS Polarity */ | |
43 | #define SDTA (1 << 10) /* Serial Data Alignment */ | |
f46a93b8 | 44 | #define PDTA (1 << 9) /* Parallel Data Alignment */ |
ae5c3223 KM |
45 | #define DEL (1 << 8) /* Serial Data Delay */ |
46 | #define CKDV(v) (v << 4) /* Serial Clock Division Ratio */ | |
47 | #define TRMD (1 << 1) /* Transmit/Receive Mode Select */ | |
48 | #define EN (1 << 0) /* SSI Module Enable */ | |
49 | ||
50 | /* | |
51 | * SSISR | |
52 | */ | |
53 | #define UIRQ (1 << 27) /* Underflow Error Interrupt Status */ | |
54 | #define OIRQ (1 << 26) /* Overflow Error Interrupt Status */ | |
55 | #define IIRQ (1 << 25) /* Idle Mode Interrupt Status */ | |
56 | #define DIRQ (1 << 24) /* Data Interrupt Status Flag */ | |
57 | ||
849fc82a KM |
58 | /* |
59 | * SSIWSR | |
60 | */ | |
61 | #define CONT (1 << 8) /* WS Continue Function */ | |
186fadc1 | 62 | #define WS_MODE (1 << 0) /* WS Mode */ |
849fc82a | 63 | |
8aefda50 KM |
64 | #define SSI_NAME "ssi" |
65 | ||
ae5c3223 | 66 | struct rsnd_ssi { |
ae5c3223 | 67 | struct rsnd_mod mod; |
940e9479 | 68 | struct rsnd_mod *dma; |
ae5c3223 | 69 | |
02534f2f | 70 | u32 flags; |
ae5c3223 KM |
71 | u32 cr_own; |
72 | u32 cr_clk; | |
e7d850dd | 73 | u32 cr_mode; |
08bada26 | 74 | u32 wsr; |
919567d9 | 75 | int chan; |
e7d850dd | 76 | int rate; |
ae5c3223 | 77 | int err; |
02534f2f | 78 | int irq; |
ae5c3223 | 79 | unsigned int usrcnt; |
ae5c3223 KM |
80 | }; |
81 | ||
02534f2f KM |
82 | /* flags */ |
83 | #define RSND_SSI_CLK_PIN_SHARE (1 << 0) | |
84 | #define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */ | |
85 | ||
ae5c3223 KM |
86 | #define for_each_rsnd_ssi(pos, priv, i) \ |
87 | for (i = 0; \ | |
88 | (i < rsnd_ssi_nr(priv)) && \ | |
dd27d808 | 89 | ((pos) = ((struct rsnd_ssi *)(priv)->ssi + i)); \ |
ae5c3223 KM |
90 | i++) |
91 | ||
02534f2f | 92 | #define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id) |
232c00b6 | 93 | #define rsnd_ssi_to_dma(mod) ((ssi)->dma) |
dd27d808 | 94 | #define rsnd_ssi_nr(priv) ((priv)->ssi_nr) |
ae5c3223 | 95 | #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) |
02534f2f | 96 | #define rsnd_ssi_mode_flags(p) ((p)->flags) |
e7d850dd | 97 | #define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io)) |
b4c83b17 | 98 | #define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io)) |
ae5c3223 | 99 | |
b415b4d3 | 100 | int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) |
d9288d0b | 101 | { |
b415b4d3 | 102 | struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); |
d9288d0b | 103 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); |
d9288d0b KM |
104 | int use_busif = 0; |
105 | ||
7b466fc6 KM |
106 | if (!rsnd_ssi_is_dma_mode(mod)) |
107 | return 0; | |
108 | ||
d9288d0b KM |
109 | if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF)) |
110 | use_busif = 1; | |
111 | if (rsnd_io_to_mod_src(io)) | |
112 | use_busif = 1; | |
113 | ||
114 | return use_busif; | |
115 | } | |
116 | ||
e10369d8 KM |
117 | static void rsnd_ssi_status_clear(struct rsnd_mod *mod) |
118 | { | |
119 | rsnd_mod_write(mod, SSISR, 0); | |
120 | } | |
121 | ||
122 | static u32 rsnd_ssi_status_get(struct rsnd_mod *mod) | |
123 | { | |
124 | return rsnd_mod_read(mod, SSISR); | |
125 | } | |
126 | ||
ae5c3223 KM |
127 | static void rsnd_ssi_status_check(struct rsnd_mod *mod, |
128 | u32 bit) | |
129 | { | |
130 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | |
131 | struct device *dev = rsnd_priv_to_dev(priv); | |
132 | u32 status; | |
133 | int i; | |
134 | ||
135 | for (i = 0; i < 1024; i++) { | |
e10369d8 | 136 | status = rsnd_ssi_status_get(mod); |
ae5c3223 KM |
137 | if (status & bit) |
138 | return; | |
139 | ||
140 | udelay(50); | |
141 | } | |
142 | ||
143 | dev_warn(dev, "status check failed\n"); | |
144 | } | |
145 | ||
37447b46 KM |
146 | static int rsnd_ssi_irq_enable(struct rsnd_mod *ssi_mod) |
147 | { | |
148 | struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); | |
149 | ||
150 | if (rsnd_is_gen1(priv)) | |
151 | return 0; | |
152 | ||
153 | /* enable SSI interrupt if Gen2 */ | |
154 | rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, | |
155 | rsnd_ssi_is_dma_mode(ssi_mod) ? | |
156 | 0x0e000000 : 0x0f000000); | |
157 | ||
158 | return 0; | |
159 | } | |
160 | ||
161 | static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod) | |
162 | { | |
163 | struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); | |
164 | ||
165 | if (rsnd_is_gen1(priv)) | |
166 | return 0; | |
167 | ||
168 | /* disable SSI interrupt if Gen2 */ | |
169 | rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000); | |
170 | ||
171 | return 0; | |
172 | } | |
173 | ||
b4c83b17 KM |
174 | u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) |
175 | { | |
176 | struct rsnd_mod *mod; | |
177 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | |
178 | struct rsnd_priv *priv = rsnd_io_to_priv(io); | |
179 | struct device *dev = rsnd_priv_to_dev(priv); | |
180 | enum rsnd_mod_type types[] = { | |
181 | RSND_MOD_SSIM1, | |
182 | RSND_MOD_SSIM2, | |
183 | RSND_MOD_SSIM3, | |
184 | }; | |
185 | int i, mask; | |
186 | ||
187 | switch (runtime->channels) { | |
188 | case 2: /* Multi channel is not needed for Stereo */ | |
189 | return 0; | |
190 | case 6: | |
191 | break; | |
192 | default: | |
193 | dev_err(dev, "unsupported channel\n"); | |
194 | return 0; | |
195 | } | |
196 | ||
197 | mask = 0; | |
198 | for (i = 0; i < ARRAY_SIZE(types); i++) { | |
199 | mod = rsnd_io_to_mod(io, types[i]); | |
200 | if (!mod) | |
201 | continue; | |
202 | ||
203 | mask |= 1 << rsnd_mod_id(mod); | |
204 | } | |
205 | ||
206 | return mask; | |
207 | } | |
208 | ||
ae5c3223 | 209 | static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, |
adcf7d5e | 210 | struct rsnd_dai_stream *io) |
ae5c3223 | 211 | { |
1b13d118 | 212 | struct rsnd_priv *priv = rsnd_io_to_priv(io); |
adcf7d5e | 213 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); |
ae5c3223 | 214 | struct device *dev = rsnd_priv_to_dev(priv); |
e7d850dd | 215 | struct rsnd_dai *rdai = rsnd_io_to_rdai(io); |
b76e218a | 216 | struct rsnd_mod *mod = rsnd_mod_get(ssi); |
e7d850dd | 217 | struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); |
c140284b | 218 | int slots = rsnd_get_slot_width(io); |
eae6fff4 | 219 | int j, ret; |
ae5c3223 KM |
220 | int ssi_clk_mul_table[] = { |
221 | 1, 2, 4, 8, 16, 6, 12, | |
222 | }; | |
223 | unsigned int main_rate; | |
ba9c949f | 224 | unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime); |
ae5c3223 | 225 | |
e7d850dd KM |
226 | if (!rsnd_rdai_is_clk_master(rdai)) |
227 | return 0; | |
228 | ||
229 | if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io)) | |
230 | return 0; | |
231 | ||
b4c83b17 KM |
232 | if (rsnd_ssi_is_multi_slave(mod, io)) |
233 | return 0; | |
234 | ||
e7d850dd KM |
235 | if (ssi->usrcnt > 1) { |
236 | if (ssi->rate != rate) { | |
237 | dev_err(dev, "SSI parent/child should use same rate\n"); | |
238 | return -EINVAL; | |
239 | } | |
240 | ||
241 | return 0; | |
242 | } | |
243 | ||
ae5c3223 KM |
244 | /* |
245 | * Find best clock, and try to start ADG | |
246 | */ | |
eae6fff4 KM |
247 | for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { |
248 | ||
249 | /* | |
250 | * this driver is assuming that | |
8ec85e7f | 251 | * system word is 32bit x slots |
eae6fff4 KM |
252 | * see rsnd_ssi_init() |
253 | */ | |
8ec85e7f | 254 | main_rate = rate * 32 * slots * ssi_clk_mul_table[j]; |
eae6fff4 KM |
255 | |
256 | ret = rsnd_adg_ssi_clk_try_start(mod, main_rate); | |
257 | if (0 == ret) { | |
258 | ssi->cr_clk = FORCE | SWL_32 | | |
259 | SCKD | SWSD | CKDV(j); | |
08bada26 | 260 | ssi->wsr = CONT; |
eae6fff4 | 261 | |
e7d850dd KM |
262 | ssi->rate = rate; |
263 | ||
eae6fff4 KM |
264 | dev_dbg(dev, "%s[%d] outputs %u Hz\n", |
265 | rsnd_mod_name(mod), | |
266 | rsnd_mod_id(mod), rate); | |
267 | ||
268 | return 0; | |
ae5c3223 KM |
269 | } |
270 | } | |
271 | ||
272 | dev_err(dev, "unsupported clock rate\n"); | |
273 | return -EIO; | |
274 | } | |
275 | ||
e7d850dd KM |
276 | static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi, |
277 | struct rsnd_dai_stream *io) | |
ae5c3223 | 278 | { |
f708d944 | 279 | struct rsnd_dai *rdai = rsnd_io_to_rdai(io); |
b76e218a | 280 | struct rsnd_mod *mod = rsnd_mod_get(ssi); |
e7d850dd | 281 | struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io); |
3ba84f45 | 282 | |
e7d850dd | 283 | if (!rsnd_rdai_is_clk_master(rdai)) |
ae5c3223 | 284 | return; |
ae5c3223 | 285 | |
e7d850dd KM |
286 | if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io)) |
287 | return; | |
ae5c3223 | 288 | |
e7d850dd KM |
289 | if (ssi->usrcnt > 1) |
290 | return; | |
919567d9 | 291 | |
e7d850dd KM |
292 | ssi->cr_clk = 0; |
293 | ssi->rate = 0; | |
ae5c3223 | 294 | |
e7d850dd | 295 | rsnd_adg_ssi_clk_stop(mod); |
ae5c3223 KM |
296 | } |
297 | ||
840ada3b KM |
298 | static int rsnd_ssi_config_init(struct rsnd_ssi *ssi, |
299 | struct rsnd_dai_stream *io) | |
300 | { | |
301 | struct rsnd_dai *rdai = rsnd_io_to_rdai(io); | |
302 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | |
303 | u32 cr_own; | |
304 | u32 cr_mode; | |
186fadc1 | 305 | u32 wsr; |
f98ed119 KM |
306 | int is_tdm; |
307 | ||
c140284b | 308 | is_tdm = (rsnd_get_slot_width(io) >= 6) ? 1 : 0; |
840ada3b KM |
309 | |
310 | /* | |
311 | * always use 32bit system word. | |
312 | * see also rsnd_ssi_master_clk_enable() | |
313 | */ | |
314 | cr_own = FORCE | SWL_32 | PDTA; | |
315 | ||
316 | if (rdai->bit_clk_inv) | |
317 | cr_own |= SCKP; | |
f98ed119 | 318 | if (rdai->frm_clk_inv ^ is_tdm) |
840ada3b KM |
319 | cr_own |= SWSP; |
320 | if (rdai->data_alignment) | |
321 | cr_own |= SDTA; | |
322 | if (rdai->sys_delay) | |
323 | cr_own |= DEL; | |
324 | if (rsnd_io_is_play(io)) | |
325 | cr_own |= TRMD; | |
326 | ||
327 | switch (runtime->sample_bits) { | |
328 | case 16: | |
329 | cr_own |= DWL_16; | |
330 | break; | |
331 | case 32: | |
332 | cr_own |= DWL_24; | |
333 | break; | |
334 | default: | |
335 | return -EINVAL; | |
336 | } | |
337 | ||
338 | if (rsnd_ssi_is_dma_mode(rsnd_mod_get(ssi))) { | |
339 | cr_mode = UIEN | OIEN | /* over/under run */ | |
340 | DMEN; /* DMA : enable DMA */ | |
341 | } else { | |
342 | cr_mode = DIEN; /* PIO : enable Data interrupt */ | |
343 | } | |
344 | ||
186fadc1 KM |
345 | /* |
346 | * TDM Extend Mode | |
347 | * see | |
348 | * rsnd_ssiu_init_gen2() | |
349 | */ | |
350 | wsr = ssi->wsr; | |
f98ed119 | 351 | if (is_tdm) { |
186fadc1 KM |
352 | wsr |= WS_MODE; |
353 | cr_own |= CHNL_8; | |
354 | } | |
355 | ||
840ada3b KM |
356 | ssi->cr_own = cr_own; |
357 | ssi->cr_mode = cr_mode; | |
186fadc1 | 358 | ssi->wsr = wsr; |
840ada3b KM |
359 | |
360 | return 0; | |
361 | } | |
362 | ||
ae5c3223 KM |
363 | /* |
364 | * SSI mod common functions | |
365 | */ | |
366 | static int rsnd_ssi_init(struct rsnd_mod *mod, | |
2c0fac19 | 367 | struct rsnd_dai_stream *io, |
690602fc | 368 | struct rsnd_priv *priv) |
ae5c3223 KM |
369 | { |
370 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | |
e7d850dd KM |
371 | int ret; |
372 | ||
373 | ssi->usrcnt++; | |
374 | ||
375 | rsnd_mod_power_on(mod); | |
376 | ||
377 | ret = rsnd_ssi_master_clk_start(ssi, io); | |
378 | if (ret < 0) | |
379 | return ret; | |
380 | ||
381 | if (rsnd_ssi_is_parent(mod, io)) | |
382 | return 0; | |
ae5c3223 | 383 | |
840ada3b KM |
384 | ret = rsnd_ssi_config_init(ssi, io); |
385 | if (ret < 0) | |
386 | return ret; | |
e7d850dd | 387 | |
ae5c3223 KM |
388 | ssi->err = -1; /* ignore 1st error */ |
389 | ||
e7d850dd KM |
390 | /* clear error status */ |
391 | rsnd_ssi_status_clear(mod); | |
392 | ||
393 | rsnd_ssi_irq_enable(mod); | |
394 | ||
ae5c3223 KM |
395 | return 0; |
396 | } | |
397 | ||
398 | static int rsnd_ssi_quit(struct rsnd_mod *mod, | |
2c0fac19 | 399 | struct rsnd_dai_stream *io, |
690602fc | 400 | struct rsnd_priv *priv) |
ae5c3223 KM |
401 | { |
402 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | |
ae5c3223 KM |
403 | struct device *dev = rsnd_priv_to_dev(priv); |
404 | ||
e5d9cfc6 AH |
405 | if (!ssi->usrcnt) { |
406 | dev_err(dev, "%s[%d] usrcnt error\n", | |
407 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | |
408 | return -EIO; | |
409 | } | |
e7d850dd | 410 | |
e5d9cfc6 AH |
411 | if (!rsnd_ssi_is_parent(mod, io)) { |
412 | if (ssi->err > 0) | |
413 | dev_warn(dev, "%s[%d] under/over flow err = %d\n", | |
414 | rsnd_mod_name(mod), rsnd_mod_id(mod), | |
415 | ssi->err); | |
ae5c3223 | 416 | |
e5d9cfc6 AH |
417 | ssi->cr_own = 0; |
418 | ssi->err = 0; | |
ae5c3223 | 419 | |
e5d9cfc6 AH |
420 | rsnd_ssi_irq_disable(mod); |
421 | } | |
e7d850dd | 422 | |
e7d850dd KM |
423 | rsnd_ssi_master_clk_stop(ssi, io); |
424 | ||
425 | rsnd_mod_power_off(mod); | |
426 | ||
427 | ssi->usrcnt--; | |
428 | ||
ae5c3223 KM |
429 | return 0; |
430 | } | |
431 | ||
919567d9 | 432 | static int rsnd_ssi_hw_params(struct rsnd_mod *mod, |
2c0fac19 | 433 | struct rsnd_dai_stream *io, |
919567d9 KM |
434 | struct snd_pcm_substream *substream, |
435 | struct snd_pcm_hw_params *params) | |
436 | { | |
437 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | |
919567d9 KM |
438 | int chan = params_channels(params); |
439 | ||
440 | /* | |
441 | * Already working. | |
442 | * It will happen if SSI has parent/child connection. | |
443 | */ | |
e7d850dd | 444 | if (ssi->usrcnt > 1) { |
919567d9 KM |
445 | /* |
446 | * it is error if child <-> parent SSI uses | |
447 | * different channels. | |
448 | */ | |
449 | if (ssi->chan != chan) | |
450 | return -EIO; | |
451 | } | |
452 | ||
919567d9 | 453 | ssi->chan = chan; |
919567d9 KM |
454 | |
455 | return 0; | |
456 | } | |
457 | ||
e10369d8 | 458 | static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi) |
ae5c3223 | 459 | { |
b76e218a | 460 | struct rsnd_mod *mod = rsnd_mod_get(ssi); |
e10369d8 | 461 | u32 status = rsnd_ssi_status_get(mod); |
b76e218a | 462 | |
ae5c3223 | 463 | /* under/over flow error */ |
5342dff2 | 464 | if (status & (UIRQ | OIRQ)) |
ae5c3223 KM |
465 | ssi->err++; |
466 | ||
e10369d8 | 467 | return status; |
ae5c3223 KM |
468 | } |
469 | ||
e7d850dd KM |
470 | static int __rsnd_ssi_start(struct rsnd_mod *mod, |
471 | struct rsnd_dai_stream *io, | |
472 | struct rsnd_priv *priv) | |
473 | { | |
474 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | |
475 | u32 cr; | |
476 | ||
477 | cr = ssi->cr_own | | |
478 | ssi->cr_clk | | |
b4c83b17 KM |
479 | ssi->cr_mode; |
480 | ||
481 | /* | |
482 | * EN will be set via SSIU :: SSI_CONTROL | |
483 | * if Multi channel mode | |
484 | */ | |
485 | if (!rsnd_ssi_multi_slaves(io)) | |
486 | cr |= EN; | |
e7d850dd KM |
487 | |
488 | rsnd_mod_write(mod, SSICR, cr); | |
08bada26 | 489 | rsnd_mod_write(mod, SSIWSR, ssi->wsr); |
e7d850dd KM |
490 | |
491 | return 0; | |
492 | } | |
493 | ||
4e7d606c | 494 | static int rsnd_ssi_start(struct rsnd_mod *mod, |
2c0fac19 | 495 | struct rsnd_dai_stream *io, |
690602fc | 496 | struct rsnd_priv *priv) |
e7d850dd KM |
497 | { |
498 | /* | |
499 | * no limit to start | |
500 | * see also | |
501 | * rsnd_ssi_stop | |
502 | * rsnd_ssi_interrupt | |
503 | */ | |
504 | return __rsnd_ssi_start(mod, io, priv); | |
505 | } | |
506 | ||
507 | static int __rsnd_ssi_stop(struct rsnd_mod *mod, | |
508 | struct rsnd_dai_stream *io, | |
509 | struct rsnd_priv *priv) | |
4e7d606c KM |
510 | { |
511 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | |
e7d850dd KM |
512 | u32 cr; |
513 | ||
514 | /* | |
515 | * disable all IRQ, | |
516 | * and, wait all data was sent | |
517 | */ | |
518 | cr = ssi->cr_own | | |
519 | ssi->cr_clk; | |
4e7d606c | 520 | |
e7d850dd KM |
521 | rsnd_mod_write(mod, SSICR, cr | EN); |
522 | rsnd_ssi_status_check(mod, DIRQ); | |
4e7d606c | 523 | |
e7d850dd KM |
524 | /* |
525 | * disable SSI, | |
526 | * and, wait idle state | |
527 | */ | |
528 | rsnd_mod_write(mod, SSICR, cr); /* disabled all */ | |
529 | rsnd_ssi_status_check(mod, IIRQ); | |
4e7d606c KM |
530 | |
531 | return 0; | |
532 | } | |
533 | ||
534 | static int rsnd_ssi_stop(struct rsnd_mod *mod, | |
2c0fac19 | 535 | struct rsnd_dai_stream *io, |
690602fc | 536 | struct rsnd_priv *priv) |
4e7d606c KM |
537 | { |
538 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | |
539 | ||
e7d850dd KM |
540 | /* |
541 | * don't stop if not last user | |
542 | * see also | |
543 | * rsnd_ssi_start | |
544 | * rsnd_ssi_interrupt | |
545 | */ | |
546 | if (ssi->usrcnt > 1) | |
547 | return 0; | |
4e7d606c | 548 | |
e7d850dd | 549 | return __rsnd_ssi_stop(mod, io, priv); |
4e7d606c KM |
550 | } |
551 | ||
bfc0cfe6 KM |
552 | static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, |
553 | struct rsnd_dai_stream *io) | |
ae5c3223 | 554 | { |
bfc0cfe6 | 555 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); |
690602fc | 556 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
69e32a58 | 557 | struct device *dev = rsnd_priv_to_dev(priv); |
765ae7c8 | 558 | int is_dma = rsnd_ssi_is_dma_mode(mod); |
02299d98 | 559 | u32 status; |
75defee0 | 560 | bool elapsed = false; |
02299d98 KM |
561 | |
562 | spin_lock(&priv->lock); | |
ae5c3223 | 563 | |
02299d98 | 564 | /* ignore all cases if not working */ |
d5bbe7de | 565 | if (!rsnd_io_is_working(io)) |
02299d98 KM |
566 | goto rsnd_ssi_interrupt_out; |
567 | ||
e10369d8 | 568 | status = rsnd_ssi_record_error(ssi); |
4e7d606c KM |
569 | |
570 | /* PIO only */ | |
765ae7c8 | 571 | if (!is_dma && (status & DIRQ)) { |
ae5c3223 KM |
572 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); |
573 | u32 *buf = (u32 *)(runtime->dma_area + | |
574 | rsnd_dai_pointer_offset(io, 0)); | |
575 | ||
ae5c3223 KM |
576 | /* |
577 | * 8/16/32 data can be assesse to TDR/RDR register | |
578 | * directly as 32bit data | |
579 | * see rsnd_ssi_init() | |
580 | */ | |
985a4f6e | 581 | if (rsnd_io_is_play(io)) |
4686a0ad | 582 | rsnd_mod_write(mod, SSITDR, *buf); |
ae5c3223 | 583 | else |
4686a0ad | 584 | *buf = rsnd_mod_read(mod, SSIRDR); |
ae5c3223 | 585 | |
75defee0 | 586 | elapsed = rsnd_dai_pointer_update(io, sizeof(*buf)); |
4e7d606c | 587 | } |
ae5c3223 | 588 | |
12927a8f KM |
589 | /* DMA only */ |
590 | if (is_dma && (status & (UIRQ | OIRQ))) { | |
4e7d606c KM |
591 | /* |
592 | * restart SSI | |
593 | */ | |
4e7d606c KM |
594 | dev_dbg(dev, "%s[%d] restart\n", |
595 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | |
044930b4 | 596 | |
e7d850dd KM |
597 | __rsnd_ssi_stop(mod, io, priv); |
598 | __rsnd_ssi_start(mod, io, priv); | |
ae5c3223 KM |
599 | } |
600 | ||
69e32a58 KM |
601 | if (ssi->err > 1024) { |
602 | rsnd_ssi_irq_disable(mod); | |
603 | ||
604 | dev_warn(dev, "no more %s[%d] restart\n", | |
605 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | |
606 | } | |
607 | ||
5342dff2 | 608 | rsnd_ssi_status_clear(mod); |
02299d98 KM |
609 | rsnd_ssi_interrupt_out: |
610 | spin_unlock(&priv->lock); | |
611 | ||
75defee0 KM |
612 | if (elapsed) |
613 | rsnd_dai_period_elapsed(io); | |
bfc0cfe6 KM |
614 | } |
615 | ||
616 | static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) | |
617 | { | |
618 | struct rsnd_mod *mod = data; | |
619 | ||
620 | rsnd_mod_interrupt(mod, __rsnd_ssi_interrupt); | |
75defee0 | 621 | |
4e7d606c | 622 | return IRQ_HANDLED; |
ae5c3223 KM |
623 | } |
624 | ||
6cfad789 KM |
625 | /* |
626 | * SSI PIO | |
627 | */ | |
e7d850dd KM |
628 | static void rsnd_ssi_parent_attach(struct rsnd_mod *mod, |
629 | struct rsnd_dai_stream *io, | |
630 | struct rsnd_priv *priv) | |
631 | { | |
632 | if (!__rsnd_ssi_is_pin_sharing(mod)) | |
633 | return; | |
634 | ||
635 | switch (rsnd_mod_id(mod)) { | |
636 | case 1: | |
637 | case 2: | |
638 | rsnd_dai_connect(rsnd_ssi_mod_get(priv, 0), io, RSND_MOD_SSIP); | |
639 | break; | |
640 | case 4: | |
641 | rsnd_dai_connect(rsnd_ssi_mod_get(priv, 3), io, RSND_MOD_SSIP); | |
642 | break; | |
643 | case 8: | |
644 | rsnd_dai_connect(rsnd_ssi_mod_get(priv, 7), io, RSND_MOD_SSIP); | |
645 | break; | |
646 | } | |
647 | } | |
648 | ||
c7f69ab5 KM |
649 | static int rsnd_ssi_common_probe(struct rsnd_mod *mod, |
650 | struct rsnd_dai_stream *io, | |
651 | struct rsnd_priv *priv) | |
ff8f30e6 | 652 | { |
ff8f30e6 KM |
653 | struct device *dev = rsnd_priv_to_dev(priv); |
654 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | |
ff8f30e6 KM |
655 | int ret; |
656 | ||
b4c83b17 KM |
657 | /* |
658 | * SSIP/SSIU/IRQ are not needed on | |
659 | * SSI Multi slaves | |
660 | */ | |
661 | if (rsnd_ssi_is_multi_slave(mod, io)) | |
662 | return 0; | |
663 | ||
e7d850dd KM |
664 | rsnd_ssi_parent_attach(mod, io, priv); |
665 | ||
c7f69ab5 KM |
666 | ret = rsnd_ssiu_attach(io, mod); |
667 | if (ret < 0) | |
668 | return ret; | |
669 | ||
02534f2f | 670 | ret = devm_request_irq(dev, ssi->irq, |
6cfad789 | 671 | rsnd_ssi_interrupt, |
ff8f30e6 | 672 | IRQF_SHARED, |
bfc0cfe6 | 673 | dev_name(dev), mod); |
8aefda50 | 674 | |
ff8f30e6 KM |
675 | return ret; |
676 | } | |
677 | ||
ae5c3223 | 678 | static struct rsnd_mod_ops rsnd_ssi_pio_ops = { |
8aefda50 | 679 | .name = SSI_NAME, |
c7f69ab5 | 680 | .probe = rsnd_ssi_common_probe, |
ae5c3223 KM |
681 | .init = rsnd_ssi_init, |
682 | .quit = rsnd_ssi_quit, | |
49229850 KM |
683 | .start = rsnd_ssi_start, |
684 | .stop = rsnd_ssi_stop, | |
919567d9 | 685 | .hw_params = rsnd_ssi_hw_params, |
ae5c3223 KM |
686 | }; |
687 | ||
ff8f30e6 | 688 | static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, |
2c0fac19 | 689 | struct rsnd_dai_stream *io, |
690602fc | 690 | struct rsnd_priv *priv) |
ff8f30e6 | 691 | { |
ff8f30e6 | 692 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); |
02534f2f | 693 | int dma_id = 0; /* not needed */ |
ff8f30e6 KM |
694 | int ret; |
695 | ||
b4c83b17 KM |
696 | /* |
697 | * SSIP/SSIU/IRQ/DMA are not needed on | |
698 | * SSI Multi slaves | |
699 | */ | |
700 | if (rsnd_ssi_is_multi_slave(mod, io)) | |
701 | return 0; | |
702 | ||
c7f69ab5 | 703 | ret = rsnd_ssi_common_probe(mod, io, priv); |
4e7d606c | 704 | if (ret) |
b543b52a | 705 | return ret; |
4e7d606c | 706 | |
355cb84f KM |
707 | /* SSI probe might be called many times in MUX multi path */ |
708 | ret = rsnd_dma_attach(io, mod, &ssi->dma, dma_id); | |
8aefda50 | 709 | |
ff8f30e6 KM |
710 | return ret; |
711 | } | |
712 | ||
713 | static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, | |
2c0fac19 | 714 | struct rsnd_dai_stream *io, |
690602fc | 715 | struct rsnd_priv *priv) |
97463e19 | 716 | { |
4e7d606c KM |
717 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); |
718 | struct device *dev = rsnd_priv_to_dev(priv); | |
02534f2f | 719 | int irq = ssi->irq; |
4e7d606c | 720 | |
4e7d606c | 721 | /* PIO will request IRQ again */ |
b05ce4c0 | 722 | devm_free_irq(dev, irq, mod); |
4e7d606c | 723 | |
97463e19 KM |
724 | return 0; |
725 | } | |
726 | ||
727 | static int rsnd_ssi_fallback(struct rsnd_mod *mod, | |
2c0fac19 | 728 | struct rsnd_dai_stream *io, |
690602fc | 729 | struct rsnd_priv *priv) |
ff8f30e6 | 730 | { |
d3a76823 KM |
731 | struct device *dev = rsnd_priv_to_dev(priv); |
732 | ||
d3a76823 KM |
733 | /* |
734 | * fallback to PIO | |
735 | * | |
736 | * SSI .probe might be called again. | |
737 | * see | |
738 | * rsnd_rdai_continuance_probe() | |
739 | */ | |
740 | mod->ops = &rsnd_ssi_pio_ops; | |
741 | ||
742 | dev_info(dev, "%s[%d] fallback to PIO mode\n", | |
743 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | |
744 | ||
ff8f30e6 KM |
745 | return 0; |
746 | } | |
747 | ||
9b99e9a7 KM |
748 | static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, |
749 | struct rsnd_mod *mod) | |
d9288d0b | 750 | { |
72adc61f | 751 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
72adc61f KM |
752 | int is_play = rsnd_io_is_play(io); |
753 | char *name; | |
754 | ||
b415b4d3 | 755 | if (rsnd_ssi_use_busif(io)) |
72adc61f KM |
756 | name = is_play ? "rxu" : "txu"; |
757 | else | |
758 | name = is_play ? "rx" : "tx"; | |
759 | ||
760 | return rsnd_dma_request_channel(rsnd_ssi_of_node(priv), | |
761 | mod, name); | |
d9288d0b KM |
762 | } |
763 | ||
849fc82a | 764 | static struct rsnd_mod_ops rsnd_ssi_dma_ops = { |
8aefda50 | 765 | .name = SSI_NAME, |
72adc61f | 766 | .dma_req = rsnd_ssi_dma_req, |
ff8f30e6 KM |
767 | .probe = rsnd_ssi_dma_probe, |
768 | .remove = rsnd_ssi_dma_remove, | |
849fc82a KM |
769 | .init = rsnd_ssi_init, |
770 | .quit = rsnd_ssi_quit, | |
497debaa KM |
771 | .start = rsnd_ssi_start, |
772 | .stop = rsnd_ssi_stop, | |
97463e19 | 773 | .fallback = rsnd_ssi_fallback, |
919567d9 | 774 | .hw_params = rsnd_ssi_hw_params, |
849fc82a KM |
775 | }; |
776 | ||
05795411 KM |
777 | int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) |
778 | { | |
779 | return mod->ops == &rsnd_ssi_dma_ops; | |
780 | } | |
781 | ||
782 | ||
ae5c3223 KM |
783 | /* |
784 | * Non SSI | |
785 | */ | |
ae5c3223 | 786 | static struct rsnd_mod_ops rsnd_ssi_non_ops = { |
8aefda50 | 787 | .name = SSI_NAME, |
ae5c3223 KM |
788 | }; |
789 | ||
790 | /* | |
791 | * ssi mod function | |
792 | */ | |
b4c83b17 KM |
793 | static void rsnd_ssi_connect(struct rsnd_mod *mod, |
794 | struct rsnd_dai_stream *io) | |
795 | { | |
796 | struct rsnd_dai *rdai = rsnd_io_to_rdai(io); | |
797 | enum rsnd_mod_type types[] = { | |
798 | RSND_MOD_SSI, | |
799 | RSND_MOD_SSIM1, | |
800 | RSND_MOD_SSIM2, | |
801 | RSND_MOD_SSIM3, | |
802 | }; | |
803 | enum rsnd_mod_type type; | |
804 | int i; | |
805 | ||
806 | /* try SSI -> SSIM1 -> SSIM2 -> SSIM3 */ | |
807 | for (i = 0; i < ARRAY_SIZE(types); i++) { | |
808 | type = types[i]; | |
809 | if (!rsnd_io_to_mod(io, type)) { | |
810 | rsnd_dai_connect(mod, io, type); | |
811 | rsnd_set_slot(rdai, 2 * (i + 1), (i + 1)); | |
812 | return; | |
813 | } | |
814 | } | |
815 | } | |
816 | ||
817 | void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, | |
818 | struct device_node *playback, | |
819 | struct device_node *capture) | |
820 | { | |
821 | struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); | |
822 | struct device_node *node; | |
823 | struct device_node *np; | |
824 | struct rsnd_mod *mod; | |
825 | int i; | |
826 | ||
827 | node = rsnd_ssi_of_node(priv); | |
828 | if (!node) | |
829 | return; | |
830 | ||
831 | i = 0; | |
832 | for_each_child_of_node(node, np) { | |
833 | mod = rsnd_ssi_mod_get(priv, i); | |
834 | if (np == playback) | |
835 | rsnd_ssi_connect(mod, &rdai->playback); | |
836 | if (np == capture) | |
837 | rsnd_ssi_connect(mod, &rdai->capture); | |
838 | i++; | |
839 | } | |
840 | ||
841 | of_node_put(node); | |
842 | } | |
843 | ||
ae5c3223 KM |
844 | struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) |
845 | { | |
8b14719b TI |
846 | if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) |
847 | id = 0; | |
ae5c3223 | 848 | |
02534f2f | 849 | return rsnd_mod_get(rsnd_ssi_get(priv, id)); |
ae5c3223 KM |
850 | } |
851 | ||
b415b4d3 | 852 | int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) |
7b5ce975 KM |
853 | { |
854 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | |
855 | ||
856 | return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE); | |
857 | } | |
858 | ||
5ba17b42 KM |
859 | static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io, |
860 | struct rsnd_mod *mod, | |
861 | enum rsnd_mod_type type) | |
862 | { | |
863 | /* | |
864 | * SSIP (= SSI parent) needs to be special, otherwise, | |
865 | * 2nd SSI might doesn't start. see also rsnd_mod_call() | |
866 | * | |
867 | * We can't include parent SSI status on SSI, because we don't know | |
868 | * how many SSI requests parent SSI. Thus, it is localed on "io" now. | |
869 | * ex) trouble case | |
870 | * Playback: SSI0 | |
871 | * Capture : SSI1 (needs SSI0) | |
872 | * | |
873 | * 1) start Capture -> SSI0/SSI1 are started. | |
874 | * 2) start Playback -> SSI0 doesn't work, because it is already | |
875 | * marked as "started" on 1) | |
876 | * | |
877 | * OTOH, using each mod's status is good for MUX case. | |
878 | * It doesn't need to start in 2nd start | |
879 | * ex) | |
880 | * IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0 | |
881 | * | | |
882 | * IO-1: SRC1 -> CTU2 -+ | |
883 | * | |
884 | * 1) start IO-0 -> start SSI0 | |
885 | * 2) start IO-1 -> SSI0 doesn't need to start, because it is | |
886 | * already started on 1) | |
887 | */ | |
888 | if (type == RSND_MOD_SSIP) | |
889 | return &io->parent_ssi_status; | |
890 | ||
891 | return rsnd_mod_get_status(io, mod, type); | |
892 | } | |
893 | ||
2ea6b074 | 894 | int rsnd_ssi_probe(struct rsnd_priv *priv) |
ae5c3223 | 895 | { |
02534f2f KM |
896 | struct device_node *node; |
897 | struct device_node *np; | |
ae5c3223 KM |
898 | struct device *dev = rsnd_priv_to_dev(priv); |
899 | struct rsnd_mod_ops *ops; | |
900 | struct clk *clk; | |
ae5c3223 KM |
901 | struct rsnd_ssi *ssi; |
902 | char name[RSND_SSI_NAME_SIZE]; | |
2f78dd7f | 903 | int i, nr, ret; |
ae5c3223 | 904 | |
02534f2f KM |
905 | node = rsnd_ssi_of_node(priv); |
906 | if (!node) | |
907 | return -EINVAL; | |
908 | ||
909 | nr = of_get_child_count(node); | |
910 | if (!nr) { | |
911 | ret = -EINVAL; | |
912 | goto rsnd_ssi_probe_done; | |
913 | } | |
90e8e50f | 914 | |
dd27d808 | 915 | ssi = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL); |
02534f2f KM |
916 | if (!ssi) { |
917 | ret = -ENOMEM; | |
918 | goto rsnd_ssi_probe_done; | |
919 | } | |
ae5c3223 | 920 | |
dd27d808 KM |
921 | priv->ssi = ssi; |
922 | priv->ssi_nr = nr; | |
ae5c3223 | 923 | |
02534f2f KM |
924 | i = 0; |
925 | for_each_child_of_node(node, np) { | |
926 | ssi = rsnd_ssi_get(priv, i); | |
ae5c3223 | 927 | |
8aefda50 KM |
928 | snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d", |
929 | SSI_NAME, i); | |
ae5c3223 | 930 | |
60dbb4f1 | 931 | clk = devm_clk_get(dev, name); |
02534f2f KM |
932 | if (IS_ERR(clk)) { |
933 | ret = PTR_ERR(clk); | |
934 | goto rsnd_ssi_probe_done; | |
935 | } | |
ae5c3223 | 936 | |
02534f2f KM |
937 | if (of_get_property(np, "shared-pin", NULL)) |
938 | ssi->flags |= RSND_SSI_CLK_PIN_SHARE; | |
939 | ||
940 | if (of_get_property(np, "no-busif", NULL)) | |
941 | ssi->flags |= RSND_SSI_NO_BUSIF; | |
942 | ||
943 | ssi->irq = irq_of_parse_and_map(np, 0); | |
944 | if (!ssi->irq) { | |
945 | ret = -EINVAL; | |
946 | goto rsnd_ssi_probe_done; | |
947 | } | |
ae5c3223 KM |
948 | |
949 | ops = &rsnd_ssi_non_ops; | |
02534f2f | 950 | if (of_get_property(np, "pio-transfer", NULL)) |
ff8f30e6 | 951 | ops = &rsnd_ssi_pio_ops; |
02534f2f KM |
952 | else |
953 | ops = &rsnd_ssi_dma_ops; | |
ae5c3223 | 954 | |
b76e218a | 955 | ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, |
5ba17b42 | 956 | rsnd_ssi_get_status, RSND_MOD_SSI, i); |
2f78dd7f | 957 | if (ret) |
02534f2f KM |
958 | goto rsnd_ssi_probe_done; |
959 | ||
960 | i++; | |
ae5c3223 KM |
961 | } |
962 | ||
02534f2f KM |
963 | ret = 0; |
964 | ||
965 | rsnd_ssi_probe_done: | |
966 | of_node_put(node); | |
967 | ||
968 | return ret; | |
ae5c3223 | 969 | } |
2f78dd7f | 970 | |
2ea6b074 | 971 | void rsnd_ssi_remove(struct rsnd_priv *priv) |
2f78dd7f KM |
972 | { |
973 | struct rsnd_ssi *ssi; | |
974 | int i; | |
975 | ||
976 | for_each_rsnd_ssi(ssi, priv, i) { | |
b76e218a | 977 | rsnd_mod_quit(rsnd_mod_get(ssi)); |
2f78dd7f KM |
978 | } |
979 | } |