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