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