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