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