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