1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Socionext UniPhier AIO ALSA common driver. |
4 | // |
5 | // Copyright (c) 2016-2018 Socionext Inc. |
6 | |
7 | #include <linux/bitfield.h> |
8 | #include <linux/errno.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/module.h> |
11 | #include <sound/core.h> |
12 | #include <sound/pcm.h> |
13 | #include <sound/pcm_params.h> |
14 | #include <sound/soc.h> |
15 | |
16 | #include "aio.h" |
17 | #include "aio-reg.h" |
18 | |
19 | static u64 rb_cnt(u64 wr, u64 rd, u64 len) |
20 | { |
21 | if (rd <= wr) |
22 | return wr - rd; |
23 | else |
24 | return len - (rd - wr); |
25 | } |
26 | |
27 | static u64 rb_cnt_to_end(u64 wr, u64 rd, u64 len) |
28 | { |
29 | if (rd <= wr) |
30 | return wr - rd; |
31 | else |
32 | return len - rd; |
33 | } |
34 | |
35 | static u64 rb_space(u64 wr, u64 rd, u64 len) |
36 | { |
37 | if (rd <= wr) |
38 | return len - (wr - rd) - 8; |
39 | else |
40 | return rd - wr - 8; |
41 | } |
42 | |
43 | static u64 rb_space_to_end(u64 wr, u64 rd, u64 len) |
44 | { |
45 | if (rd > wr) |
46 | return rd - wr - 8; |
47 | else if (rd > 0) |
48 | return len - wr; |
49 | else |
50 | return len - wr - 8; |
51 | } |
52 | |
53 | u64 aio_rb_cnt(struct uniphier_aio_sub *sub) |
54 | { |
55 | return rb_cnt(wr: sub->wr_offs, rd: sub->rd_offs, len: sub->compr_bytes); |
56 | } |
57 | |
58 | u64 aio_rbt_cnt_to_end(struct uniphier_aio_sub *sub) |
59 | { |
60 | return rb_cnt_to_end(wr: sub->wr_offs, rd: sub->rd_offs, len: sub->compr_bytes); |
61 | } |
62 | |
63 | u64 aio_rb_space(struct uniphier_aio_sub *sub) |
64 | { |
65 | return rb_space(wr: sub->wr_offs, rd: sub->rd_offs, len: sub->compr_bytes); |
66 | } |
67 | |
68 | u64 aio_rb_space_to_end(struct uniphier_aio_sub *sub) |
69 | { |
70 | return rb_space_to_end(wr: sub->wr_offs, rd: sub->rd_offs, len: sub->compr_bytes); |
71 | } |
72 | |
73 | /** |
74 | * aio_iecout_set_enable - setup IEC output via SoC glue |
75 | * @chip: the AIO chip pointer |
76 | * @enable: false to stop the output, true to start |
77 | * |
78 | * Set enabled or disabled S/PDIF signal output to out of SoC via AOnIEC pins. |
79 | * This function need to call at driver startup. |
80 | * |
81 | * The regmap of SoC glue is specified by 'socionext,syscon' optional property |
82 | * of DT. This function has no effect if no property. |
83 | */ |
84 | void aio_iecout_set_enable(struct uniphier_aio_chip *chip, bool enable) |
85 | { |
86 | struct regmap *r = chip->regmap_sg; |
87 | |
88 | if (!r) |
89 | return; |
90 | |
91 | regmap_write(map: r, SG_AOUTEN, val: (enable) ? ~0 : 0); |
92 | } |
93 | |
94 | /** |
95 | * aio_chip_set_pll - set frequency to audio PLL |
96 | * @chip: the AIO chip pointer |
97 | * @pll_id: PLL |
98 | * @freq: frequency in Hz, 0 is ignored |
99 | * |
100 | * Sets frequency of audio PLL. This function can be called anytime, |
101 | * but it takes time till PLL is locked. |
102 | * |
103 | * Return: Zero if successful, otherwise a negative value on error. |
104 | */ |
105 | int aio_chip_set_pll(struct uniphier_aio_chip *chip, int pll_id, |
106 | unsigned int freq) |
107 | { |
108 | struct device *dev = &chip->pdev->dev; |
109 | struct regmap *r = chip->regmap; |
110 | int shift; |
111 | u32 v; |
112 | |
113 | /* Not change */ |
114 | if (freq == 0) |
115 | return 0; |
116 | |
117 | switch (pll_id) { |
118 | case AUD_PLL_A1: |
119 | shift = 0; |
120 | break; |
121 | case AUD_PLL_F1: |
122 | shift = 1; |
123 | break; |
124 | case AUD_PLL_A2: |
125 | shift = 2; |
126 | break; |
127 | case AUD_PLL_F2: |
128 | shift = 3; |
129 | break; |
130 | default: |
131 | dev_err(dev, "PLL(%d) not supported\n" , pll_id); |
132 | return -EINVAL; |
133 | } |
134 | |
135 | switch (freq) { |
136 | case 36864000: |
137 | v = A2APLLCTR1_APLLX_36MHZ; |
138 | break; |
139 | case 33868800: |
140 | v = A2APLLCTR1_APLLX_33MHZ; |
141 | break; |
142 | default: |
143 | dev_err(dev, "PLL frequency not supported(%d)\n" , freq); |
144 | return -EINVAL; |
145 | } |
146 | chip->plls[pll_id].freq = freq; |
147 | |
148 | regmap_update_bits(map: r, A2APLLCTR1, A2APLLCTR1_APLLX_MASK << shift, |
149 | val: v << shift); |
150 | |
151 | return 0; |
152 | } |
153 | |
154 | /** |
155 | * aio_chip_init - initialize AIO whole settings |
156 | * @chip: the AIO chip pointer |
157 | * |
158 | * Sets AIO fixed and whole device settings to AIO. |
159 | * This function need to call once at driver startup. |
160 | * |
161 | * The register area that is changed by this function is shared by all |
162 | * modules of AIO. But there is not race condition since this function |
163 | * has always set the same initialize values. |
164 | */ |
165 | void aio_chip_init(struct uniphier_aio_chip *chip) |
166 | { |
167 | struct regmap *r = chip->regmap; |
168 | |
169 | regmap_update_bits(map: r, A2APLLCTR0, |
170 | A2APLLCTR0_APLLXPOW_MASK, |
171 | A2APLLCTR0_APLLXPOW_PWON); |
172 | |
173 | regmap_update_bits(map: r, A2EXMCLKSEL0, |
174 | A2EXMCLKSEL0_EXMCLK_MASK, |
175 | A2EXMCLKSEL0_EXMCLK_OUTPUT); |
176 | |
177 | regmap_update_bits(map: r, A2AIOINPUTSEL, A2AIOINPUTSEL_RXSEL_MASK, |
178 | A2AIOINPUTSEL_RXSEL_PCMI1_HDMIRX1 | |
179 | A2AIOINPUTSEL_RXSEL_PCMI2_SIF | |
180 | A2AIOINPUTSEL_RXSEL_PCMI3_EVEA | |
181 | A2AIOINPUTSEL_RXSEL_IECI1_HDMIRX1); |
182 | |
183 | if (chip->chip_spec->addr_ext) |
184 | regmap_update_bits(map: r, CDA2D_TEST, CDA2D_TEST_DDR_MODE_MASK, |
185 | CDA2D_TEST_DDR_MODE_EXTON0); |
186 | else |
187 | regmap_update_bits(map: r, CDA2D_TEST, CDA2D_TEST_DDR_MODE_MASK, |
188 | CDA2D_TEST_DDR_MODE_EXTOFF1); |
189 | } |
190 | |
191 | /** |
192 | * aio_init - initialize AIO substream |
193 | * @sub: the AIO substream pointer |
194 | * |
195 | * Sets fixed settings of each AIO substreams. |
196 | * This function need to call once at substream startup. |
197 | * |
198 | * Return: Zero if successful, otherwise a negative value on error. |
199 | */ |
200 | int aio_init(struct uniphier_aio_sub *sub) |
201 | { |
202 | struct device *dev = &sub->aio->chip->pdev->dev; |
203 | struct regmap *r = sub->aio->chip->regmap; |
204 | |
205 | regmap_write(map: r, A2RBNMAPCTR0(sub->swm->rb.hw), |
206 | MAPCTR0_EN | sub->swm->rb.map); |
207 | regmap_write(map: r, A2CHNMAPCTR0(sub->swm->ch.hw), |
208 | MAPCTR0_EN | sub->swm->ch.map); |
209 | |
210 | switch (sub->swm->type) { |
211 | case PORT_TYPE_I2S: |
212 | case PORT_TYPE_SPDIF: |
213 | case PORT_TYPE_EVE: |
214 | if (sub->swm->dir == PORT_DIR_INPUT) { |
215 | regmap_write(map: r, A2IIFNMAPCTR0(sub->swm->iif.hw), |
216 | MAPCTR0_EN | sub->swm->iif.map); |
217 | regmap_write(map: r, A2IPORTNMAPCTR0(sub->swm->iport.hw), |
218 | MAPCTR0_EN | sub->swm->iport.map); |
219 | } else { |
220 | regmap_write(map: r, A2OIFNMAPCTR0(sub->swm->oif.hw), |
221 | MAPCTR0_EN | sub->swm->oif.map); |
222 | regmap_write(map: r, A2OPORTNMAPCTR0(sub->swm->oport.hw), |
223 | MAPCTR0_EN | sub->swm->oport.map); |
224 | } |
225 | break; |
226 | case PORT_TYPE_CONV: |
227 | regmap_write(map: r, A2OIFNMAPCTR0(sub->swm->oif.hw), |
228 | MAPCTR0_EN | sub->swm->oif.map); |
229 | regmap_write(map: r, A2OPORTNMAPCTR0(sub->swm->oport.hw), |
230 | MAPCTR0_EN | sub->swm->oport.map); |
231 | regmap_write(map: r, A2CHNMAPCTR0(sub->swm->och.hw), |
232 | MAPCTR0_EN | sub->swm->och.map); |
233 | regmap_write(map: r, A2IIFNMAPCTR0(sub->swm->iif.hw), |
234 | MAPCTR0_EN | sub->swm->iif.map); |
235 | break; |
236 | default: |
237 | dev_err(dev, "Unknown port type %d.\n" , sub->swm->type); |
238 | return -EINVAL; |
239 | } |
240 | |
241 | return 0; |
242 | } |
243 | |
244 | /** |
245 | * aio_port_reset - reset AIO port block |
246 | * @sub: the AIO substream pointer |
247 | * |
248 | * Resets the digital signal input/output port block of AIO. |
249 | */ |
250 | void aio_port_reset(struct uniphier_aio_sub *sub) |
251 | { |
252 | struct regmap *r = sub->aio->chip->regmap; |
253 | |
254 | if (sub->swm->dir == PORT_DIR_OUTPUT) { |
255 | regmap_write(map: r, AOUTRSTCTR0, BIT(sub->swm->oport.map)); |
256 | regmap_write(map: r, AOUTRSTCTR1, BIT(sub->swm->oport.map)); |
257 | } else { |
258 | regmap_update_bits(map: r, IPORTMXRSTCTR(sub->swm->iport.map), |
259 | IPORTMXRSTCTR_RSTPI_MASK, |
260 | IPORTMXRSTCTR_RSTPI_RESET); |
261 | regmap_update_bits(map: r, IPORTMXRSTCTR(sub->swm->iport.map), |
262 | IPORTMXRSTCTR_RSTPI_MASK, |
263 | IPORTMXRSTCTR_RSTPI_RELEASE); |
264 | } |
265 | } |
266 | |
267 | /** |
268 | * aio_port_set_ch - set channels of LPCM |
269 | * @sub: the AIO substream pointer, PCM substream only |
270 | * |
271 | * Set suitable slot selecting to input/output port block of AIO. |
272 | * |
273 | * This function may return error if non-PCM substream. |
274 | * |
275 | * Return: Zero if successful, otherwise a negative value on error. |
276 | */ |
277 | static int aio_port_set_ch(struct uniphier_aio_sub *sub) |
278 | { |
279 | struct regmap *r = sub->aio->chip->regmap; |
280 | static const u32 slotsel_2ch[] = { |
281 | 0, 0, 0, 0, 0, |
282 | }; |
283 | static const u32 slotsel_multi[] = { |
284 | OPORTMXTYSLOTCTR_SLOTSEL_SLOT0, |
285 | OPORTMXTYSLOTCTR_SLOTSEL_SLOT1, |
286 | OPORTMXTYSLOTCTR_SLOTSEL_SLOT2, |
287 | OPORTMXTYSLOTCTR_SLOTSEL_SLOT3, |
288 | OPORTMXTYSLOTCTR_SLOTSEL_SLOT4, |
289 | }; |
290 | u32 mode; |
291 | const u32 *slotsel; |
292 | int i; |
293 | |
294 | switch (params_channels(p: &sub->params)) { |
295 | case 8: |
296 | case 6: |
297 | mode = OPORTMXTYSLOTCTR_MODE; |
298 | slotsel = slotsel_multi; |
299 | break; |
300 | case 2: |
301 | mode = 0; |
302 | slotsel = slotsel_2ch; |
303 | break; |
304 | default: |
305 | return -EINVAL; |
306 | } |
307 | |
308 | for (i = 0; i < AUD_MAX_SLOTSEL; i++) { |
309 | regmap_update_bits(map: r, OPORTMXTYSLOTCTR(sub->swm->oport.map, i), |
310 | OPORTMXTYSLOTCTR_MODE, val: mode); |
311 | regmap_update_bits(map: r, OPORTMXTYSLOTCTR(sub->swm->oport.map, i), |
312 | OPORTMXTYSLOTCTR_SLOTSEL_MASK, val: slotsel[i]); |
313 | } |
314 | |
315 | return 0; |
316 | } |
317 | |
318 | /** |
319 | * aio_port_set_rate - set sampling rate of LPCM |
320 | * @sub: the AIO substream pointer, PCM substream only |
321 | * @rate: Sampling rate in Hz. |
322 | * |
323 | * Set suitable I2S format settings to input/output port block of AIO. |
324 | * Parameter is specified by hw_params(). |
325 | * |
326 | * This function may return error if non-PCM substream. |
327 | * |
328 | * Return: Zero if successful, otherwise a negative value on error. |
329 | */ |
330 | static int aio_port_set_rate(struct uniphier_aio_sub *sub, int rate) |
331 | { |
332 | struct regmap *r = sub->aio->chip->regmap; |
333 | struct device *dev = &sub->aio->chip->pdev->dev; |
334 | u32 v; |
335 | |
336 | if (sub->swm->dir == PORT_DIR_OUTPUT) { |
337 | switch (rate) { |
338 | case 8000: |
339 | v = OPORTMXCTR1_FSSEL_8; |
340 | break; |
341 | case 11025: |
342 | v = OPORTMXCTR1_FSSEL_11_025; |
343 | break; |
344 | case 12000: |
345 | v = OPORTMXCTR1_FSSEL_12; |
346 | break; |
347 | case 16000: |
348 | v = OPORTMXCTR1_FSSEL_16; |
349 | break; |
350 | case 22050: |
351 | v = OPORTMXCTR1_FSSEL_22_05; |
352 | break; |
353 | case 24000: |
354 | v = OPORTMXCTR1_FSSEL_24; |
355 | break; |
356 | case 32000: |
357 | v = OPORTMXCTR1_FSSEL_32; |
358 | break; |
359 | case 44100: |
360 | v = OPORTMXCTR1_FSSEL_44_1; |
361 | break; |
362 | case 48000: |
363 | v = OPORTMXCTR1_FSSEL_48; |
364 | break; |
365 | case 88200: |
366 | v = OPORTMXCTR1_FSSEL_88_2; |
367 | break; |
368 | case 96000: |
369 | v = OPORTMXCTR1_FSSEL_96; |
370 | break; |
371 | case 176400: |
372 | v = OPORTMXCTR1_FSSEL_176_4; |
373 | break; |
374 | case 192000: |
375 | v = OPORTMXCTR1_FSSEL_192; |
376 | break; |
377 | default: |
378 | dev_err(dev, "Rate not supported(%d)\n" , rate); |
379 | return -EINVAL; |
380 | } |
381 | |
382 | regmap_update_bits(map: r, OPORTMXCTR1(sub->swm->oport.map), |
383 | OPORTMXCTR1_FSSEL_MASK, val: v); |
384 | } else { |
385 | switch (rate) { |
386 | case 8000: |
387 | v = IPORTMXCTR1_FSSEL_8; |
388 | break; |
389 | case 11025: |
390 | v = IPORTMXCTR1_FSSEL_11_025; |
391 | break; |
392 | case 12000: |
393 | v = IPORTMXCTR1_FSSEL_12; |
394 | break; |
395 | case 16000: |
396 | v = IPORTMXCTR1_FSSEL_16; |
397 | break; |
398 | case 22050: |
399 | v = IPORTMXCTR1_FSSEL_22_05; |
400 | break; |
401 | case 24000: |
402 | v = IPORTMXCTR1_FSSEL_24; |
403 | break; |
404 | case 32000: |
405 | v = IPORTMXCTR1_FSSEL_32; |
406 | break; |
407 | case 44100: |
408 | v = IPORTMXCTR1_FSSEL_44_1; |
409 | break; |
410 | case 48000: |
411 | v = IPORTMXCTR1_FSSEL_48; |
412 | break; |
413 | case 88200: |
414 | v = IPORTMXCTR1_FSSEL_88_2; |
415 | break; |
416 | case 96000: |
417 | v = IPORTMXCTR1_FSSEL_96; |
418 | break; |
419 | case 176400: |
420 | v = IPORTMXCTR1_FSSEL_176_4; |
421 | break; |
422 | case 192000: |
423 | v = IPORTMXCTR1_FSSEL_192; |
424 | break; |
425 | default: |
426 | dev_err(dev, "Rate not supported(%d)\n" , rate); |
427 | return -EINVAL; |
428 | } |
429 | |
430 | regmap_update_bits(map: r, IPORTMXCTR1(sub->swm->iport.map), |
431 | IPORTMXCTR1_FSSEL_MASK, val: v); |
432 | } |
433 | |
434 | return 0; |
435 | } |
436 | |
437 | /** |
438 | * aio_port_set_fmt - set format of I2S data |
439 | * @sub: the AIO substream pointer, PCM substream only |
440 | * This parameter has no effect if substream is I2S or PCM. |
441 | * |
442 | * Set suitable I2S format settings to input/output port block of AIO. |
443 | * Parameter is specified by set_fmt(). |
444 | * |
445 | * This function may return error if non-PCM substream. |
446 | * |
447 | * Return: Zero if successful, otherwise a negative value on error. |
448 | */ |
449 | static int aio_port_set_fmt(struct uniphier_aio_sub *sub) |
450 | { |
451 | struct regmap *r = sub->aio->chip->regmap; |
452 | struct device *dev = &sub->aio->chip->pdev->dev; |
453 | u32 v; |
454 | |
455 | if (sub->swm->dir == PORT_DIR_OUTPUT) { |
456 | switch (sub->aio->fmt) { |
457 | case SND_SOC_DAIFMT_LEFT_J: |
458 | v = OPORTMXCTR1_I2SLRSEL_LEFT; |
459 | break; |
460 | case SND_SOC_DAIFMT_RIGHT_J: |
461 | v = OPORTMXCTR1_I2SLRSEL_RIGHT; |
462 | break; |
463 | case SND_SOC_DAIFMT_I2S: |
464 | v = OPORTMXCTR1_I2SLRSEL_I2S; |
465 | break; |
466 | default: |
467 | dev_err(dev, "Format is not supported(%d)\n" , |
468 | sub->aio->fmt); |
469 | return -EINVAL; |
470 | } |
471 | |
472 | v |= OPORTMXCTR1_OUTBITSEL_24; |
473 | regmap_update_bits(map: r, OPORTMXCTR1(sub->swm->oport.map), |
474 | OPORTMXCTR1_I2SLRSEL_MASK | |
475 | OPORTMXCTR1_OUTBITSEL_MASK, val: v); |
476 | } else { |
477 | switch (sub->aio->fmt) { |
478 | case SND_SOC_DAIFMT_LEFT_J: |
479 | v = IPORTMXCTR1_LRSEL_LEFT; |
480 | break; |
481 | case SND_SOC_DAIFMT_RIGHT_J: |
482 | v = IPORTMXCTR1_LRSEL_RIGHT; |
483 | break; |
484 | case SND_SOC_DAIFMT_I2S: |
485 | v = IPORTMXCTR1_LRSEL_I2S; |
486 | break; |
487 | default: |
488 | dev_err(dev, "Format is not supported(%d)\n" , |
489 | sub->aio->fmt); |
490 | return -EINVAL; |
491 | } |
492 | |
493 | v |= IPORTMXCTR1_OUTBITSEL_24 | |
494 | IPORTMXCTR1_CHSEL_ALL; |
495 | regmap_update_bits(map: r, IPORTMXCTR1(sub->swm->iport.map), |
496 | IPORTMXCTR1_LRSEL_MASK | |
497 | IPORTMXCTR1_OUTBITSEL_MASK | |
498 | IPORTMXCTR1_CHSEL_MASK, val: v); |
499 | } |
500 | |
501 | return 0; |
502 | } |
503 | |
504 | /** |
505 | * aio_port_set_clk - set clock and divider of AIO port block |
506 | * @sub: the AIO substream pointer |
507 | * |
508 | * Set suitable PLL clock divider and relational settings to |
509 | * input/output port block of AIO. Parameters are specified by |
510 | * set_sysclk() and set_pll(). |
511 | * |
512 | * Return: Zero if successful, otherwise a negative value on error. |
513 | */ |
514 | static int aio_port_set_clk(struct uniphier_aio_sub *sub) |
515 | { |
516 | struct uniphier_aio_chip *chip = sub->aio->chip; |
517 | struct device *dev = &sub->aio->chip->pdev->dev; |
518 | struct regmap *r = sub->aio->chip->regmap; |
519 | static const u32 v_pll[] = { |
520 | OPORTMXCTR2_ACLKSEL_A1, OPORTMXCTR2_ACLKSEL_F1, |
521 | OPORTMXCTR2_ACLKSEL_A2, OPORTMXCTR2_ACLKSEL_F2, |
522 | OPORTMXCTR2_ACLKSEL_A2PLL, |
523 | OPORTMXCTR2_ACLKSEL_RX1, |
524 | }; |
525 | static const u32 v_div[] = { |
526 | OPORTMXCTR2_DACCKSEL_1_2, OPORTMXCTR2_DACCKSEL_1_3, |
527 | OPORTMXCTR2_DACCKSEL_1_1, OPORTMXCTR2_DACCKSEL_2_3, |
528 | }; |
529 | u32 v; |
530 | |
531 | if (sub->swm->dir == PORT_DIR_OUTPUT) { |
532 | if (sub->swm->type == PORT_TYPE_I2S) { |
533 | if (sub->aio->pll_out >= ARRAY_SIZE(v_pll)) { |
534 | dev_err(dev, "PLL(%d) is invalid\n" , |
535 | sub->aio->pll_out); |
536 | return -EINVAL; |
537 | } |
538 | if (sub->aio->plldiv >= ARRAY_SIZE(v_div)) { |
539 | dev_err(dev, "PLL divider(%d) is invalid\n" , |
540 | sub->aio->plldiv); |
541 | return -EINVAL; |
542 | } |
543 | |
544 | v = v_pll[sub->aio->pll_out] | |
545 | OPORTMXCTR2_MSSEL_MASTER | |
546 | v_div[sub->aio->plldiv]; |
547 | |
548 | switch (chip->plls[sub->aio->pll_out].freq) { |
549 | case 0: |
550 | case 36864000: |
551 | case 33868800: |
552 | v |= OPORTMXCTR2_EXTLSIFSSEL_36; |
553 | break; |
554 | default: |
555 | v |= OPORTMXCTR2_EXTLSIFSSEL_24; |
556 | break; |
557 | } |
558 | } else if (sub->swm->type == PORT_TYPE_EVE) { |
559 | v = OPORTMXCTR2_ACLKSEL_A2PLL | |
560 | OPORTMXCTR2_MSSEL_MASTER | |
561 | OPORTMXCTR2_EXTLSIFSSEL_36 | |
562 | OPORTMXCTR2_DACCKSEL_1_2; |
563 | } else if (sub->swm->type == PORT_TYPE_SPDIF) { |
564 | if (sub->aio->pll_out >= ARRAY_SIZE(v_pll)) { |
565 | dev_err(dev, "PLL(%d) is invalid\n" , |
566 | sub->aio->pll_out); |
567 | return -EINVAL; |
568 | } |
569 | v = v_pll[sub->aio->pll_out] | |
570 | OPORTMXCTR2_MSSEL_MASTER | |
571 | OPORTMXCTR2_DACCKSEL_1_2; |
572 | |
573 | switch (chip->plls[sub->aio->pll_out].freq) { |
574 | case 0: |
575 | case 36864000: |
576 | case 33868800: |
577 | v |= OPORTMXCTR2_EXTLSIFSSEL_36; |
578 | break; |
579 | default: |
580 | v |= OPORTMXCTR2_EXTLSIFSSEL_24; |
581 | break; |
582 | } |
583 | } else { |
584 | v = OPORTMXCTR2_ACLKSEL_A1 | |
585 | OPORTMXCTR2_MSSEL_MASTER | |
586 | OPORTMXCTR2_EXTLSIFSSEL_36 | |
587 | OPORTMXCTR2_DACCKSEL_1_2; |
588 | } |
589 | regmap_write(map: r, OPORTMXCTR2(sub->swm->oport.map), val: v); |
590 | } else { |
591 | v = IPORTMXCTR2_ACLKSEL_A1 | |
592 | IPORTMXCTR2_MSSEL_SLAVE | |
593 | IPORTMXCTR2_EXTLSIFSSEL_36 | |
594 | IPORTMXCTR2_DACCKSEL_1_2; |
595 | regmap_write(map: r, IPORTMXCTR2(sub->swm->iport.map), val: v); |
596 | } |
597 | |
598 | return 0; |
599 | } |
600 | |
601 | /** |
602 | * aio_port_set_param - set parameters of AIO port block |
603 | * @sub: the AIO substream pointer |
604 | * @pass_through: Zero if sound data is LPCM, otherwise if data is not LPCM. |
605 | * This parameter has no effect if substream is I2S or PCM. |
606 | * @params: hardware parameters of ALSA |
607 | * |
608 | * Set suitable setting to input/output port block of AIO to process the |
609 | * specified in params. |
610 | * |
611 | * Return: Zero if successful, otherwise a negative value on error. |
612 | */ |
613 | int aio_port_set_param(struct uniphier_aio_sub *sub, int pass_through, |
614 | const struct snd_pcm_hw_params *params) |
615 | { |
616 | struct regmap *r = sub->aio->chip->regmap; |
617 | unsigned int rate; |
618 | u32 v; |
619 | int ret; |
620 | |
621 | if (!pass_through) { |
622 | if (sub->swm->type == PORT_TYPE_EVE || |
623 | sub->swm->type == PORT_TYPE_CONV) { |
624 | rate = 48000; |
625 | } else { |
626 | rate = params_rate(p: params); |
627 | } |
628 | |
629 | ret = aio_port_set_ch(sub); |
630 | if (ret) |
631 | return ret; |
632 | |
633 | ret = aio_port_set_rate(sub, rate); |
634 | if (ret) |
635 | return ret; |
636 | |
637 | ret = aio_port_set_fmt(sub); |
638 | if (ret) |
639 | return ret; |
640 | } |
641 | |
642 | ret = aio_port_set_clk(sub); |
643 | if (ret) |
644 | return ret; |
645 | |
646 | if (sub->swm->dir == PORT_DIR_OUTPUT) { |
647 | if (pass_through) |
648 | v = OPORTMXCTR3_SRCSEL_STREAM | |
649 | OPORTMXCTR3_VALID_STREAM; |
650 | else |
651 | v = OPORTMXCTR3_SRCSEL_PCM | |
652 | OPORTMXCTR3_VALID_PCM; |
653 | |
654 | v |= OPORTMXCTR3_IECTHUR_IECOUT | |
655 | OPORTMXCTR3_PMSEL_PAUSE | |
656 | OPORTMXCTR3_PMSW_MUTE_OFF; |
657 | regmap_write(map: r, OPORTMXCTR3(sub->swm->oport.map), val: v); |
658 | } else { |
659 | regmap_write(map: r, IPORTMXACLKSEL0EX(sub->swm->iport.map), |
660 | IPORTMXACLKSEL0EX_ACLKSEL0EX_INTERNAL); |
661 | regmap_write(map: r, IPORTMXEXNOE(sub->swm->iport.map), |
662 | IPORTMXEXNOE_PCMINOE_INPUT); |
663 | } |
664 | |
665 | return 0; |
666 | } |
667 | |
668 | /** |
669 | * aio_port_set_enable - start or stop of AIO port block |
670 | * @sub: the AIO substream pointer |
671 | * @enable: zero to stop the block, otherwise to start |
672 | * |
673 | * Start or stop the signal input/output port block of AIO. |
674 | */ |
675 | void aio_port_set_enable(struct uniphier_aio_sub *sub, int enable) |
676 | { |
677 | struct regmap *r = sub->aio->chip->regmap; |
678 | |
679 | if (sub->swm->dir == PORT_DIR_OUTPUT) { |
680 | regmap_write(map: r, OPORTMXPATH(sub->swm->oport.map), |
681 | val: sub->swm->oif.map); |
682 | |
683 | regmap_update_bits(map: r, OPORTMXMASK(sub->swm->oport.map), |
684 | OPORTMXMASK_IUDXMSK_MASK | |
685 | OPORTMXMASK_IUXCKMSK_MASK | |
686 | OPORTMXMASK_DXMSK_MASK | |
687 | OPORTMXMASK_XCKMSK_MASK, |
688 | OPORTMXMASK_IUDXMSK_OFF | |
689 | OPORTMXMASK_IUXCKMSK_OFF | |
690 | OPORTMXMASK_DXMSK_OFF | |
691 | OPORTMXMASK_XCKMSK_OFF); |
692 | |
693 | if (enable) |
694 | regmap_write(map: r, AOUTENCTR0, BIT(sub->swm->oport.map)); |
695 | else |
696 | regmap_write(map: r, AOUTENCTR1, BIT(sub->swm->oport.map)); |
697 | } else { |
698 | regmap_update_bits(map: r, IPORTMXMASK(sub->swm->iport.map), |
699 | IPORTMXMASK_IUXCKMSK_MASK | |
700 | IPORTMXMASK_XCKMSK_MASK, |
701 | IPORTMXMASK_IUXCKMSK_OFF | |
702 | IPORTMXMASK_XCKMSK_OFF); |
703 | |
704 | if (enable) |
705 | regmap_update_bits(map: r, |
706 | IPORTMXCTR2(sub->swm->iport.map), |
707 | IPORTMXCTR2_REQEN_MASK, |
708 | IPORTMXCTR2_REQEN_ENABLE); |
709 | else |
710 | regmap_update_bits(map: r, |
711 | IPORTMXCTR2(sub->swm->iport.map), |
712 | IPORTMXCTR2_REQEN_MASK, |
713 | IPORTMXCTR2_REQEN_DISABLE); |
714 | } |
715 | } |
716 | |
717 | /** |
718 | * aio_port_get_volume - get volume of AIO port block |
719 | * @sub: the AIO substream pointer |
720 | * |
721 | * Return: current volume, range is 0x0000 - 0xffff |
722 | */ |
723 | int aio_port_get_volume(struct uniphier_aio_sub *sub) |
724 | { |
725 | struct regmap *r = sub->aio->chip->regmap; |
726 | u32 v; |
727 | |
728 | regmap_read(map: r, OPORTMXTYVOLGAINSTATUS(sub->swm->oport.map, 0), val: &v); |
729 | |
730 | return FIELD_GET(OPORTMXTYVOLGAINSTATUS_CUR_MASK, v); |
731 | } |
732 | |
733 | /** |
734 | * aio_port_set_volume - set volume of AIO port block |
735 | * @sub: the AIO substream pointer |
736 | * @vol: target volume, range is 0x0000 - 0xffff. |
737 | * |
738 | * Change digital volume and perfome fade-out/fade-in effect for specified |
739 | * output slot of port. Gained PCM value can calculate as the following: |
740 | * Gained = Original * vol / 0x4000 |
741 | */ |
742 | void aio_port_set_volume(struct uniphier_aio_sub *sub, int vol) |
743 | { |
744 | struct regmap *r = sub->aio->chip->regmap; |
745 | int oport_map = sub->swm->oport.map; |
746 | int cur, diff, slope = 0, fs; |
747 | |
748 | if (sub->swm->dir == PORT_DIR_INPUT) |
749 | return; |
750 | |
751 | cur = aio_port_get_volume(sub); |
752 | diff = abs(vol - cur); |
753 | fs = params_rate(p: &sub->params); |
754 | if (fs) |
755 | slope = diff / AUD_VOL_FADE_TIME * 1000 / fs; |
756 | slope = max(1, slope); |
757 | |
758 | regmap_update_bits(map: r, OPORTMXTYVOLPARA1(oport_map, 0), |
759 | OPORTMXTYVOLPARA1_SLOPEU_MASK, val: slope << 16); |
760 | regmap_update_bits(map: r, OPORTMXTYVOLPARA2(oport_map, 0), |
761 | OPORTMXTYVOLPARA2_TARGET_MASK, val: vol); |
762 | |
763 | if (cur < vol) |
764 | regmap_update_bits(map: r, OPORTMXTYVOLPARA2(oport_map, 0), |
765 | OPORTMXTYVOLPARA2_FADE_MASK, |
766 | OPORTMXTYVOLPARA2_FADE_FADEIN); |
767 | else |
768 | regmap_update_bits(map: r, OPORTMXTYVOLPARA2(oport_map, 0), |
769 | OPORTMXTYVOLPARA2_FADE_MASK, |
770 | OPORTMXTYVOLPARA2_FADE_FADEOUT); |
771 | |
772 | regmap_write(map: r, AOUTFADECTR0, BIT(oport_map)); |
773 | } |
774 | |
775 | /** |
776 | * aio_if_set_param - set parameters of AIO DMA I/F block |
777 | * @sub: the AIO substream pointer |
778 | * @pass_through: Zero if sound data is LPCM, otherwise if data is not LPCM. |
779 | * This parameter has no effect if substream is I2S or PCM. |
780 | * |
781 | * Set suitable setting to DMA interface block of AIO to process the |
782 | * specified in settings. |
783 | * |
784 | * Return: Zero if successful, otherwise a negative value on error. |
785 | */ |
786 | int aio_if_set_param(struct uniphier_aio_sub *sub, int pass_through) |
787 | { |
788 | struct regmap *r = sub->aio->chip->regmap; |
789 | u32 memfmt, v; |
790 | |
791 | if (sub->swm->dir == PORT_DIR_OUTPUT) { |
792 | if (pass_through) { |
793 | v = PBOUTMXCTR0_ENDIAN_0123 | |
794 | PBOUTMXCTR0_MEMFMT_STREAM; |
795 | } else { |
796 | switch (params_channels(p: &sub->params)) { |
797 | case 2: |
798 | memfmt = PBOUTMXCTR0_MEMFMT_2CH; |
799 | break; |
800 | case 6: |
801 | memfmt = PBOUTMXCTR0_MEMFMT_6CH; |
802 | break; |
803 | case 8: |
804 | memfmt = PBOUTMXCTR0_MEMFMT_8CH; |
805 | break; |
806 | default: |
807 | return -EINVAL; |
808 | } |
809 | v = PBOUTMXCTR0_ENDIAN_3210 | memfmt; |
810 | } |
811 | |
812 | regmap_write(map: r, PBOUTMXCTR0(sub->swm->oif.map), val: v); |
813 | regmap_write(map: r, PBOUTMXCTR1(sub->swm->oif.map), val: 0); |
814 | } else { |
815 | regmap_write(map: r, PBINMXCTR(sub->swm->iif.map), |
816 | PBINMXCTR_NCONNECT_CONNECT | |
817 | PBINMXCTR_INOUTSEL_IN | |
818 | (sub->swm->iport.map << PBINMXCTR_PBINSEL_SHIFT) | |
819 | PBINMXCTR_ENDIAN_3210 | |
820 | PBINMXCTR_MEMFMT_D0); |
821 | } |
822 | |
823 | return 0; |
824 | } |
825 | |
826 | /** |
827 | * aio_oport_set_stream_type - set parameters of AIO playback port block |
828 | * @sub: the AIO substream pointer |
829 | * @pc: Pc type of IEC61937 |
830 | * |
831 | * Set special setting to output port block of AIO to output the stream |
832 | * via S/PDIF. |
833 | * |
834 | * Return: Zero if successful, otherwise a negative value on error. |
835 | */ |
836 | int aio_oport_set_stream_type(struct uniphier_aio_sub *sub, |
837 | enum IEC61937_PC pc) |
838 | { |
839 | struct regmap *r = sub->aio->chip->regmap; |
840 | u32 repet = 0, pause = OPORTMXPAUDAT_PAUSEPC_CMN; |
841 | |
842 | switch (pc) { |
843 | case IEC61937_PC_AC3: |
844 | repet = OPORTMXREPET_STRLENGTH_AC3 | |
845 | OPORTMXREPET_PMLENGTH_AC3; |
846 | pause |= OPORTMXPAUDAT_PAUSEPD_AC3; |
847 | break; |
848 | case IEC61937_PC_MPA: |
849 | repet = OPORTMXREPET_STRLENGTH_MPA | |
850 | OPORTMXREPET_PMLENGTH_MPA; |
851 | pause |= OPORTMXPAUDAT_PAUSEPD_MPA; |
852 | break; |
853 | case IEC61937_PC_MP3: |
854 | repet = OPORTMXREPET_STRLENGTH_MP3 | |
855 | OPORTMXREPET_PMLENGTH_MP3; |
856 | pause |= OPORTMXPAUDAT_PAUSEPD_MP3; |
857 | break; |
858 | case IEC61937_PC_DTS1: |
859 | repet = OPORTMXREPET_STRLENGTH_DTS1 | |
860 | OPORTMXREPET_PMLENGTH_DTS1; |
861 | pause |= OPORTMXPAUDAT_PAUSEPD_DTS1; |
862 | break; |
863 | case IEC61937_PC_DTS2: |
864 | repet = OPORTMXREPET_STRLENGTH_DTS2 | |
865 | OPORTMXREPET_PMLENGTH_DTS2; |
866 | pause |= OPORTMXPAUDAT_PAUSEPD_DTS2; |
867 | break; |
868 | case IEC61937_PC_DTS3: |
869 | repet = OPORTMXREPET_STRLENGTH_DTS3 | |
870 | OPORTMXREPET_PMLENGTH_DTS3; |
871 | pause |= OPORTMXPAUDAT_PAUSEPD_DTS3; |
872 | break; |
873 | case IEC61937_PC_AAC: |
874 | repet = OPORTMXREPET_STRLENGTH_AAC | |
875 | OPORTMXREPET_PMLENGTH_AAC; |
876 | pause |= OPORTMXPAUDAT_PAUSEPD_AAC; |
877 | break; |
878 | case IEC61937_PC_PAUSE: |
879 | /* Do nothing */ |
880 | break; |
881 | } |
882 | |
883 | regmap_write(map: r, OPORTMXREPET(sub->swm->oport.map), val: repet); |
884 | regmap_write(map: r, OPORTMXPAUDAT(sub->swm->oport.map), val: pause); |
885 | |
886 | return 0; |
887 | } |
888 | |
889 | /** |
890 | * aio_src_reset - reset AIO SRC block |
891 | * @sub: the AIO substream pointer |
892 | * |
893 | * Resets the digital signal input/output port with sampling rate converter |
894 | * block of AIO. |
895 | * This function has no effect if substream is not supported rate converter. |
896 | */ |
897 | void aio_src_reset(struct uniphier_aio_sub *sub) |
898 | { |
899 | struct regmap *r = sub->aio->chip->regmap; |
900 | |
901 | if (sub->swm->dir != PORT_DIR_OUTPUT) |
902 | return; |
903 | |
904 | regmap_write(map: r, AOUTSRCRSTCTR0, BIT(sub->swm->oport.map)); |
905 | regmap_write(map: r, AOUTSRCRSTCTR1, BIT(sub->swm->oport.map)); |
906 | } |
907 | |
908 | /** |
909 | * aio_src_set_param - set parameters of AIO SRC block |
910 | * @sub: the AIO substream pointer |
911 | * @params: hardware parameters of ALSA |
912 | * |
913 | * Set suitable setting to input/output port with sampling rate converter |
914 | * block of AIO to process the specified in params. |
915 | * This function has no effect if substream is not supported rate converter. |
916 | * |
917 | * Return: Zero if successful, otherwise a negative value on error. |
918 | */ |
919 | int aio_src_set_param(struct uniphier_aio_sub *sub, |
920 | const struct snd_pcm_hw_params *params) |
921 | { |
922 | struct regmap *r = sub->aio->chip->regmap; |
923 | u32 v; |
924 | |
925 | if (sub->swm->dir != PORT_DIR_OUTPUT) |
926 | return 0; |
927 | |
928 | regmap_write(map: r, OPORTMXSRC1CTR(sub->swm->oport.map), |
929 | OPORTMXSRC1CTR_THMODE_SRC | |
930 | OPORTMXSRC1CTR_SRCPATH_CALC | |
931 | OPORTMXSRC1CTR_SYNC_ASYNC | |
932 | OPORTMXSRC1CTR_FSIIPSEL_INNER | |
933 | OPORTMXSRC1CTR_FSISEL_ACLK); |
934 | |
935 | switch (params_rate(p: params)) { |
936 | default: |
937 | case 48000: |
938 | v = OPORTMXRATE_I_ACLKSEL_APLLA1 | |
939 | OPORTMXRATE_I_MCKSEL_36 | |
940 | OPORTMXRATE_I_FSSEL_48; |
941 | break; |
942 | case 44100: |
943 | v = OPORTMXRATE_I_ACLKSEL_APLLA2 | |
944 | OPORTMXRATE_I_MCKSEL_33 | |
945 | OPORTMXRATE_I_FSSEL_44_1; |
946 | break; |
947 | case 32000: |
948 | v = OPORTMXRATE_I_ACLKSEL_APLLA1 | |
949 | OPORTMXRATE_I_MCKSEL_36 | |
950 | OPORTMXRATE_I_FSSEL_32; |
951 | break; |
952 | } |
953 | |
954 | regmap_write(map: r, OPORTMXRATE_I(sub->swm->oport.map), |
955 | val: v | OPORTMXRATE_I_ACLKSRC_APLL | |
956 | OPORTMXRATE_I_LRCKSTP_STOP); |
957 | regmap_update_bits(map: r, OPORTMXRATE_I(sub->swm->oport.map), |
958 | OPORTMXRATE_I_LRCKSTP_MASK, |
959 | OPORTMXRATE_I_LRCKSTP_START); |
960 | |
961 | return 0; |
962 | } |
963 | |
964 | int aio_srcif_set_param(struct uniphier_aio_sub *sub) |
965 | { |
966 | struct regmap *r = sub->aio->chip->regmap; |
967 | |
968 | regmap_write(map: r, PBINMXCTR(sub->swm->iif.map), |
969 | PBINMXCTR_NCONNECT_CONNECT | |
970 | PBINMXCTR_INOUTSEL_OUT | |
971 | (sub->swm->oport.map << PBINMXCTR_PBINSEL_SHIFT) | |
972 | PBINMXCTR_ENDIAN_3210 | |
973 | PBINMXCTR_MEMFMT_D0); |
974 | |
975 | return 0; |
976 | } |
977 | |
978 | int aio_srcch_set_param(struct uniphier_aio_sub *sub) |
979 | { |
980 | struct regmap *r = sub->aio->chip->regmap; |
981 | |
982 | regmap_write(map: r, CDA2D_CHMXCTRL1(sub->swm->och.map), |
983 | CDA2D_CHMXCTRL1_INDSIZE_INFINITE); |
984 | |
985 | regmap_write(map: r, CDA2D_CHMXSRCAMODE(sub->swm->och.map), |
986 | CDA2D_CHMXAMODE_ENDIAN_3210 | |
987 | CDA2D_CHMXAMODE_AUPDT_FIX | |
988 | CDA2D_CHMXAMODE_TYPE_NORMAL); |
989 | |
990 | regmap_write(map: r, CDA2D_CHMXDSTAMODE(sub->swm->och.map), |
991 | CDA2D_CHMXAMODE_ENDIAN_3210 | |
992 | CDA2D_CHMXAMODE_AUPDT_INC | |
993 | CDA2D_CHMXAMODE_TYPE_RING | |
994 | (sub->swm->och.map << CDA2D_CHMXAMODE_RSSEL_SHIFT)); |
995 | |
996 | return 0; |
997 | } |
998 | |
999 | void aio_srcch_set_enable(struct uniphier_aio_sub *sub, int enable) |
1000 | { |
1001 | struct regmap *r = sub->aio->chip->regmap; |
1002 | u32 v; |
1003 | |
1004 | if (enable) |
1005 | v = CDA2D_STRT0_STOP_START; |
1006 | else |
1007 | v = CDA2D_STRT0_STOP_STOP; |
1008 | |
1009 | regmap_write(map: r, CDA2D_STRT0, |
1010 | val: v | BIT(sub->swm->och.map)); |
1011 | } |
1012 | |
1013 | int aiodma_ch_set_param(struct uniphier_aio_sub *sub) |
1014 | { |
1015 | struct regmap *r = sub->aio->chip->regmap; |
1016 | u32 v; |
1017 | |
1018 | regmap_write(map: r, CDA2D_CHMXCTRL1(sub->swm->ch.map), |
1019 | CDA2D_CHMXCTRL1_INDSIZE_INFINITE); |
1020 | |
1021 | v = CDA2D_CHMXAMODE_ENDIAN_3210 | |
1022 | CDA2D_CHMXAMODE_AUPDT_INC | |
1023 | CDA2D_CHMXAMODE_TYPE_NORMAL | |
1024 | (sub->swm->rb.map << CDA2D_CHMXAMODE_RSSEL_SHIFT); |
1025 | if (sub->swm->dir == PORT_DIR_OUTPUT) |
1026 | regmap_write(map: r, CDA2D_CHMXSRCAMODE(sub->swm->ch.map), val: v); |
1027 | else |
1028 | regmap_write(map: r, CDA2D_CHMXDSTAMODE(sub->swm->ch.map), val: v); |
1029 | |
1030 | return 0; |
1031 | } |
1032 | |
1033 | void aiodma_ch_set_enable(struct uniphier_aio_sub *sub, int enable) |
1034 | { |
1035 | struct regmap *r = sub->aio->chip->regmap; |
1036 | |
1037 | if (enable) { |
1038 | regmap_write(map: r, CDA2D_STRT0, |
1039 | CDA2D_STRT0_STOP_START | BIT(sub->swm->ch.map)); |
1040 | |
1041 | regmap_update_bits(map: r, INTRBIM(0), |
1042 | BIT(sub->swm->rb.map), |
1043 | BIT(sub->swm->rb.map)); |
1044 | } else { |
1045 | regmap_write(map: r, CDA2D_STRT0, |
1046 | CDA2D_STRT0_STOP_STOP | BIT(sub->swm->ch.map)); |
1047 | |
1048 | regmap_update_bits(map: r, INTRBIM(0), |
1049 | BIT(sub->swm->rb.map), |
1050 | val: 0); |
1051 | } |
1052 | } |
1053 | |
1054 | static u64 aiodma_rb_get_rp(struct uniphier_aio_sub *sub) |
1055 | { |
1056 | struct regmap *r = sub->aio->chip->regmap; |
1057 | u32 pos_u, pos_l; |
1058 | int i; |
1059 | |
1060 | regmap_write(map: r, CDA2D_RDPTRLOAD, |
1061 | CDA2D_RDPTRLOAD_LSFLAG_STORE | BIT(sub->swm->rb.map)); |
1062 | /* Wait for setup */ |
1063 | for (i = 0; i < 6; i++) |
1064 | regmap_read(map: r, CDA2D_RBMXRDPTR(sub->swm->rb.map), val: &pos_l); |
1065 | |
1066 | regmap_read(map: r, CDA2D_RBMXRDPTR(sub->swm->rb.map), val: &pos_l); |
1067 | regmap_read(map: r, CDA2D_RBMXRDPTRU(sub->swm->rb.map), val: &pos_u); |
1068 | pos_u = FIELD_GET(CDA2D_RBMXPTRU_PTRU_MASK, pos_u); |
1069 | |
1070 | return ((u64)pos_u << 32) | pos_l; |
1071 | } |
1072 | |
1073 | static void aiodma_rb_set_rp(struct uniphier_aio_sub *sub, u64 pos) |
1074 | { |
1075 | struct regmap *r = sub->aio->chip->regmap; |
1076 | u32 tmp; |
1077 | int i; |
1078 | |
1079 | regmap_write(map: r, CDA2D_RBMXRDPTR(sub->swm->rb.map), val: (u32)pos); |
1080 | regmap_write(map: r, CDA2D_RBMXRDPTRU(sub->swm->rb.map), val: (u32)(pos >> 32)); |
1081 | regmap_write(map: r, CDA2D_RDPTRLOAD, BIT(sub->swm->rb.map)); |
1082 | /* Wait for setup */ |
1083 | for (i = 0; i < 6; i++) |
1084 | regmap_read(map: r, CDA2D_RBMXRDPTR(sub->swm->rb.map), val: &tmp); |
1085 | } |
1086 | |
1087 | static u64 aiodma_rb_get_wp(struct uniphier_aio_sub *sub) |
1088 | { |
1089 | struct regmap *r = sub->aio->chip->regmap; |
1090 | u32 pos_u, pos_l; |
1091 | int i; |
1092 | |
1093 | regmap_write(map: r, CDA2D_WRPTRLOAD, |
1094 | CDA2D_WRPTRLOAD_LSFLAG_STORE | BIT(sub->swm->rb.map)); |
1095 | /* Wait for setup */ |
1096 | for (i = 0; i < 6; i++) |
1097 | regmap_read(map: r, CDA2D_RBMXWRPTR(sub->swm->rb.map), val: &pos_l); |
1098 | |
1099 | regmap_read(map: r, CDA2D_RBMXWRPTR(sub->swm->rb.map), val: &pos_l); |
1100 | regmap_read(map: r, CDA2D_RBMXWRPTRU(sub->swm->rb.map), val: &pos_u); |
1101 | pos_u = FIELD_GET(CDA2D_RBMXPTRU_PTRU_MASK, pos_u); |
1102 | |
1103 | return ((u64)pos_u << 32) | pos_l; |
1104 | } |
1105 | |
1106 | static void aiodma_rb_set_wp(struct uniphier_aio_sub *sub, u64 pos) |
1107 | { |
1108 | struct regmap *r = sub->aio->chip->regmap; |
1109 | u32 tmp; |
1110 | int i; |
1111 | |
1112 | regmap_write(map: r, CDA2D_RBMXWRPTR(sub->swm->rb.map), |
1113 | lower_32_bits(pos)); |
1114 | regmap_write(map: r, CDA2D_RBMXWRPTRU(sub->swm->rb.map), |
1115 | upper_32_bits(pos)); |
1116 | regmap_write(map: r, CDA2D_WRPTRLOAD, BIT(sub->swm->rb.map)); |
1117 | /* Wait for setup */ |
1118 | for (i = 0; i < 6; i++) |
1119 | regmap_read(map: r, CDA2D_RBMXWRPTR(sub->swm->rb.map), val: &tmp); |
1120 | } |
1121 | |
1122 | int aiodma_rb_set_threshold(struct uniphier_aio_sub *sub, u64 size, u32 th) |
1123 | { |
1124 | struct regmap *r = sub->aio->chip->regmap; |
1125 | |
1126 | if (size <= th) |
1127 | return -EINVAL; |
1128 | |
1129 | regmap_write(map: r, CDA2D_RBMXBTH(sub->swm->rb.map), val: th); |
1130 | regmap_write(map: r, CDA2D_RBMXRTH(sub->swm->rb.map), val: th); |
1131 | |
1132 | return 0; |
1133 | } |
1134 | |
1135 | int aiodma_rb_set_buffer(struct uniphier_aio_sub *sub, u64 start, u64 end, |
1136 | int period) |
1137 | { |
1138 | struct regmap *r = sub->aio->chip->regmap; |
1139 | u64 size = end - start; |
1140 | int ret; |
1141 | |
1142 | if (end < start || period < 0) |
1143 | return -EINVAL; |
1144 | |
1145 | regmap_write(map: r, CDA2D_RBMXCNFG(sub->swm->rb.map), val: 0); |
1146 | regmap_write(map: r, CDA2D_RBMXBGNADRS(sub->swm->rb.map), |
1147 | lower_32_bits(start)); |
1148 | regmap_write(map: r, CDA2D_RBMXBGNADRSU(sub->swm->rb.map), |
1149 | upper_32_bits(start)); |
1150 | regmap_write(map: r, CDA2D_RBMXENDADRS(sub->swm->rb.map), |
1151 | lower_32_bits(end)); |
1152 | regmap_write(map: r, CDA2D_RBMXENDADRSU(sub->swm->rb.map), |
1153 | upper_32_bits(end)); |
1154 | |
1155 | regmap_write(map: r, CDA2D_RBADRSLOAD, BIT(sub->swm->rb.map)); |
1156 | |
1157 | ret = aiodma_rb_set_threshold(sub, size, th: 2 * period); |
1158 | if (ret) |
1159 | return ret; |
1160 | |
1161 | if (sub->swm->dir == PORT_DIR_OUTPUT) { |
1162 | aiodma_rb_set_rp(sub, pos: start); |
1163 | aiodma_rb_set_wp(sub, pos: end - period); |
1164 | |
1165 | regmap_update_bits(map: r, CDA2D_RBMXIE(sub->swm->rb.map), |
1166 | CDA2D_RBMXIX_SPACE, |
1167 | CDA2D_RBMXIX_SPACE); |
1168 | } else { |
1169 | aiodma_rb_set_rp(sub, pos: end - period); |
1170 | aiodma_rb_set_wp(sub, pos: start); |
1171 | |
1172 | regmap_update_bits(map: r, CDA2D_RBMXIE(sub->swm->rb.map), |
1173 | CDA2D_RBMXIX_REMAIN, |
1174 | CDA2D_RBMXIX_REMAIN); |
1175 | } |
1176 | |
1177 | sub->threshold = 2 * period; |
1178 | sub->rd_offs = 0; |
1179 | sub->wr_offs = 0; |
1180 | sub->rd_org = 0; |
1181 | sub->wr_org = 0; |
1182 | sub->rd_total = 0; |
1183 | sub->wr_total = 0; |
1184 | |
1185 | return 0; |
1186 | } |
1187 | |
1188 | void aiodma_rb_sync(struct uniphier_aio_sub *sub, u64 start, u64 size, |
1189 | int period) |
1190 | { |
1191 | if (sub->swm->dir == PORT_DIR_OUTPUT) { |
1192 | sub->rd_offs = aiodma_rb_get_rp(sub) - start; |
1193 | |
1194 | if (sub->use_mmap) { |
1195 | sub->threshold = 2 * period; |
1196 | aiodma_rb_set_threshold(sub, size, th: 2 * period); |
1197 | |
1198 | sub->wr_offs = sub->rd_offs - period; |
1199 | if (sub->rd_offs < period) |
1200 | sub->wr_offs += size; |
1201 | } |
1202 | aiodma_rb_set_wp(sub, pos: sub->wr_offs + start); |
1203 | } else { |
1204 | sub->wr_offs = aiodma_rb_get_wp(sub) - start; |
1205 | |
1206 | if (sub->use_mmap) { |
1207 | sub->threshold = 2 * period; |
1208 | aiodma_rb_set_threshold(sub, size, th: 2 * period); |
1209 | |
1210 | sub->rd_offs = sub->wr_offs - period; |
1211 | if (sub->wr_offs < period) |
1212 | sub->rd_offs += size; |
1213 | } |
1214 | aiodma_rb_set_rp(sub, pos: sub->rd_offs + start); |
1215 | } |
1216 | |
1217 | sub->rd_total += sub->rd_offs - sub->rd_org; |
1218 | if (sub->rd_offs < sub->rd_org) |
1219 | sub->rd_total += size; |
1220 | sub->wr_total += sub->wr_offs - sub->wr_org; |
1221 | if (sub->wr_offs < sub->wr_org) |
1222 | sub->wr_total += size; |
1223 | |
1224 | sub->rd_org = sub->rd_offs; |
1225 | sub->wr_org = sub->wr_offs; |
1226 | } |
1227 | |
1228 | bool aiodma_rb_is_irq(struct uniphier_aio_sub *sub) |
1229 | { |
1230 | struct regmap *r = sub->aio->chip->regmap; |
1231 | u32 ir; |
1232 | |
1233 | regmap_read(map: r, CDA2D_RBMXIR(sub->swm->rb.map), val: &ir); |
1234 | |
1235 | if (sub->swm->dir == PORT_DIR_OUTPUT) |
1236 | return !!(ir & CDA2D_RBMXIX_SPACE); |
1237 | else |
1238 | return !!(ir & CDA2D_RBMXIX_REMAIN); |
1239 | } |
1240 | |
1241 | void aiodma_rb_clear_irq(struct uniphier_aio_sub *sub) |
1242 | { |
1243 | struct regmap *r = sub->aio->chip->regmap; |
1244 | |
1245 | if (sub->swm->dir == PORT_DIR_OUTPUT) |
1246 | regmap_write(map: r, CDA2D_RBMXIR(sub->swm->rb.map), |
1247 | CDA2D_RBMXIX_SPACE); |
1248 | else |
1249 | regmap_write(map: r, CDA2D_RBMXIR(sub->swm->rb.map), |
1250 | CDA2D_RBMXIX_REMAIN); |
1251 | } |
1252 | |