1
2
3#include <linux/debugfs.h>
4#include <linux/dma-mapping.h>
5#include <linux/init.h>
6#include <linux/io.h>
7#include <linux/module.h>
8#include <linux/slab.h>
9#include <linux/timer.h>
10#include <sound/core.h>
11#include <sound/pcm.h>
12#include <sound/pcm_params.h>
13#include <sound/soc.h>
14#include <sound/soc-dai.h>
15
16#include "cygnus-ssp.h"
17
18
19
20#define INTH_R5F_STATUS_OFFSET 0x040
21#define INTH_R5F_CLEAR_OFFSET 0x048
22#define INTH_R5F_MASK_SET_OFFSET 0x050
23#define INTH_R5F_MASK_CLEAR_OFFSET 0x054
24
25#define BF_REARM_FREE_MARK_OFFSET 0x344
26#define BF_REARM_FULL_MARK_OFFSET 0x348
27
28
29
30#define SRC_RBUF_0_RDADDR_OFFSET 0x500
31#define SRC_RBUF_1_RDADDR_OFFSET 0x518
32#define SRC_RBUF_2_RDADDR_OFFSET 0x530
33#define SRC_RBUF_3_RDADDR_OFFSET 0x548
34#define SRC_RBUF_4_RDADDR_OFFSET 0x560
35#define SRC_RBUF_5_RDADDR_OFFSET 0x578
36#define SRC_RBUF_6_RDADDR_OFFSET 0x590
37
38
39#define SRC_RBUF_0_WRADDR_OFFSET 0x504
40#define SRC_RBUF_1_WRADDR_OFFSET 0x51c
41#define SRC_RBUF_2_WRADDR_OFFSET 0x534
42#define SRC_RBUF_3_WRADDR_OFFSET 0x54c
43#define SRC_RBUF_4_WRADDR_OFFSET 0x564
44#define SRC_RBUF_5_WRADDR_OFFSET 0x57c
45#define SRC_RBUF_6_WRADDR_OFFSET 0x594
46
47
48#define SRC_RBUF_0_BASEADDR_OFFSET 0x508
49#define SRC_RBUF_1_BASEADDR_OFFSET 0x520
50#define SRC_RBUF_2_BASEADDR_OFFSET 0x538
51#define SRC_RBUF_3_BASEADDR_OFFSET 0x550
52#define SRC_RBUF_4_BASEADDR_OFFSET 0x568
53#define SRC_RBUF_5_BASEADDR_OFFSET 0x580
54#define SRC_RBUF_6_BASEADDR_OFFSET 0x598
55
56
57#define SRC_RBUF_0_ENDADDR_OFFSET 0x50c
58#define SRC_RBUF_1_ENDADDR_OFFSET 0x524
59#define SRC_RBUF_2_ENDADDR_OFFSET 0x53c
60#define SRC_RBUF_3_ENDADDR_OFFSET 0x554
61#define SRC_RBUF_4_ENDADDR_OFFSET 0x56c
62#define SRC_RBUF_5_ENDADDR_OFFSET 0x584
63#define SRC_RBUF_6_ENDADDR_OFFSET 0x59c
64
65
66#define SRC_RBUF_0_FREE_MARK_OFFSET 0x510
67#define SRC_RBUF_1_FREE_MARK_OFFSET 0x528
68#define SRC_RBUF_2_FREE_MARK_OFFSET 0x540
69#define SRC_RBUF_3_FREE_MARK_OFFSET 0x558
70#define SRC_RBUF_4_FREE_MARK_OFFSET 0x570
71#define SRC_RBUF_5_FREE_MARK_OFFSET 0x588
72#define SRC_RBUF_6_FREE_MARK_OFFSET 0x5a0
73
74
75#define DST_RBUF_0_RDADDR_OFFSET 0x5c0
76#define DST_RBUF_1_RDADDR_OFFSET 0x5d8
77#define DST_RBUF_2_RDADDR_OFFSET 0x5f0
78#define DST_RBUF_3_RDADDR_OFFSET 0x608
79#define DST_RBUF_4_RDADDR_OFFSET 0x620
80#define DST_RBUF_5_RDADDR_OFFSET 0x638
81
82
83#define DST_RBUF_0_WRADDR_OFFSET 0x5c4
84#define DST_RBUF_1_WRADDR_OFFSET 0x5dc
85#define DST_RBUF_2_WRADDR_OFFSET 0x5f4
86#define DST_RBUF_3_WRADDR_OFFSET 0x60c
87#define DST_RBUF_4_WRADDR_OFFSET 0x624
88#define DST_RBUF_5_WRADDR_OFFSET 0x63c
89
90
91#define DST_RBUF_0_BASEADDR_OFFSET 0x5c8
92#define DST_RBUF_1_BASEADDR_OFFSET 0x5e0
93#define DST_RBUF_2_BASEADDR_OFFSET 0x5f8
94#define DST_RBUF_3_BASEADDR_OFFSET 0x610
95#define DST_RBUF_4_BASEADDR_OFFSET 0x628
96#define DST_RBUF_5_BASEADDR_OFFSET 0x640
97
98
99#define DST_RBUF_0_ENDADDR_OFFSET 0x5cc
100#define DST_RBUF_1_ENDADDR_OFFSET 0x5e4
101#define DST_RBUF_2_ENDADDR_OFFSET 0x5fc
102#define DST_RBUF_3_ENDADDR_OFFSET 0x614
103#define DST_RBUF_4_ENDADDR_OFFSET 0x62c
104#define DST_RBUF_5_ENDADDR_OFFSET 0x644
105
106
107#define DST_RBUF_0_FULL_MARK_OFFSET 0x5d0
108#define DST_RBUF_1_FULL_MARK_OFFSET 0x5e8
109#define DST_RBUF_2_FULL_MARK_OFFSET 0x600
110#define DST_RBUF_3_FULL_MARK_OFFSET 0x618
111#define DST_RBUF_4_FULL_MARK_OFFSET 0x630
112#define DST_RBUF_5_FULL_MARK_OFFSET 0x648
113
114
115
116
117#define ESR0_STATUS_OFFSET 0x900
118#define ESR1_STATUS_OFFSET 0x918
119#define ESR2_STATUS_OFFSET 0x930
120#define ESR3_STATUS_OFFSET 0x948
121#define ESR4_STATUS_OFFSET 0x960
122
123
124#define ESR0_STATUS_CLR_OFFSET 0x908
125#define ESR1_STATUS_CLR_OFFSET 0x920
126#define ESR2_STATUS_CLR_OFFSET 0x938
127#define ESR3_STATUS_CLR_OFFSET 0x950
128#define ESR4_STATUS_CLR_OFFSET 0x968
129
130
131#define ESR0_MASK_STATUS_OFFSET 0x90c
132#define ESR1_MASK_STATUS_OFFSET 0x924
133#define ESR2_MASK_STATUS_OFFSET 0x93c
134#define ESR3_MASK_STATUS_OFFSET 0x954
135#define ESR4_MASK_STATUS_OFFSET 0x96c
136
137
138#define ESR0_MASK_SET_OFFSET 0x910
139#define ESR1_MASK_SET_OFFSET 0x928
140#define ESR2_MASK_SET_OFFSET 0x940
141#define ESR3_MASK_SET_OFFSET 0x958
142#define ESR4_MASK_SET_OFFSET 0x970
143
144
145#define ESR0_MASK_CLR_OFFSET 0x914
146#define ESR1_MASK_CLR_OFFSET 0x92c
147#define ESR2_MASK_CLR_OFFSET 0x944
148#define ESR3_MASK_CLR_OFFSET 0x95c
149#define ESR4_MASK_CLR_OFFSET 0x974
150
151
152#define R5F_ESR0_SHIFT 0
153#define R5F_ESR1_SHIFT 1
154#define R5F_ESR2_SHIFT 2
155#define R5F_ESR3_SHIFT 3
156#define R5F_ESR4_SHIFT 4
157
158
159
160#define ANY_PLAYBACK_IRQ (BIT(R5F_ESR0_SHIFT) | \
161 BIT(R5F_ESR1_SHIFT) | \
162 BIT(R5F_ESR3_SHIFT))
163
164
165#define ANY_CAPTURE_IRQ (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT))
166
167
168
169
170
171#define PERIOD_BYTES_MIN 0x100
172
173static const struct snd_pcm_hardware cygnus_pcm_hw = {
174 .info = SNDRV_PCM_INFO_MMAP |
175 SNDRV_PCM_INFO_MMAP_VALID |
176 SNDRV_PCM_INFO_INTERLEAVED,
177 .formats = SNDRV_PCM_FMTBIT_S16_LE |
178 SNDRV_PCM_FMTBIT_S32_LE,
179
180
181 .period_bytes_min = PERIOD_BYTES_MIN,
182 .period_bytes_max = 0x10000,
183
184
185 .periods_min = 2,
186 .periods_max = 8,
187
188
189
190
191
192 .buffer_bytes_max = 4 * 0x8000,
193};
194
195static u64 cygnus_dma_dmamask = DMA_BIT_MASK(32);
196
197static struct cygnus_aio_port *cygnus_dai_get_dma_data(
198 struct snd_pcm_substream *substream)
199{
200 struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
201
202 return snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(soc_runtime, 0), substream);
203}
204
205static void ringbuf_set_initial(void __iomem *audio_io,
206 struct ringbuf_regs *p_rbuf,
207 bool is_playback,
208 u32 start,
209 u32 periodsize,
210 u32 bufsize)
211{
212 u32 initial_rd;
213 u32 initial_wr;
214 u32 end;
215 u32 fmark_val;
216
217 p_rbuf->period_bytes = periodsize;
218 p_rbuf->buf_size = bufsize;
219
220 if (is_playback) {
221
222 initial_rd = start;
223 initial_wr = initial_rd ^ BIT(31);
224 } else {
225
226 initial_wr = start;
227 initial_rd = initial_wr;
228 }
229
230 end = start + bufsize - 1;
231
232
233
234
235
236
237 fmark_val = periodsize - PERIOD_BYTES_MIN;
238
239 writel(start, audio_io + p_rbuf->baseaddr);
240 writel(end, audio_io + p_rbuf->endaddr);
241 writel(fmark_val, audio_io + p_rbuf->fmark);
242 writel(initial_rd, audio_io + p_rbuf->rdaddr);
243 writel(initial_wr, audio_io + p_rbuf->wraddr);
244}
245
246static int configure_ringbuf_regs(struct snd_pcm_substream *substream)
247{
248 struct cygnus_aio_port *aio;
249 struct ringbuf_regs *p_rbuf;
250 int status = 0;
251
252 aio = cygnus_dai_get_dma_data(substream);
253
254
255 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
256 p_rbuf = &aio->play_rb_regs;
257
258 switch (aio->portnum) {
259 case 0:
260 *p_rbuf = RINGBUF_REG_PLAYBACK(0);
261 break;
262 case 1:
263 *p_rbuf = RINGBUF_REG_PLAYBACK(2);
264 break;
265 case 2:
266 *p_rbuf = RINGBUF_REG_PLAYBACK(4);
267 break;
268 case 3:
269 *p_rbuf = RINGBUF_REG_PLAYBACK(6);
270 break;
271 default:
272 status = -EINVAL;
273 }
274 } else {
275 p_rbuf = &aio->capture_rb_regs;
276
277 switch (aio->portnum) {
278 case 0:
279 *p_rbuf = RINGBUF_REG_CAPTURE(0);
280 break;
281 case 1:
282 *p_rbuf = RINGBUF_REG_CAPTURE(2);
283 break;
284 case 2:
285 *p_rbuf = RINGBUF_REG_CAPTURE(4);
286 break;
287 default:
288 status = -EINVAL;
289 }
290 }
291
292 return status;
293}
294
295static struct ringbuf_regs *get_ringbuf(struct snd_pcm_substream *substream)
296{
297 struct cygnus_aio_port *aio;
298 struct ringbuf_regs *p_rbuf = NULL;
299
300 aio = cygnus_dai_get_dma_data(substream);
301
302 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
303 p_rbuf = &aio->play_rb_regs;
304 else
305 p_rbuf = &aio->capture_rb_regs;
306
307 return p_rbuf;
308}
309
310static void enable_intr(struct snd_pcm_substream *substream)
311{
312 struct cygnus_aio_port *aio;
313 u32 clear_mask;
314
315 aio = cygnus_dai_get_dma_data(substream);
316
317
318 clear_mask = BIT(aio->portnum);
319
320 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
321
322 writel(clear_mask, aio->cygaud->audio + ESR0_STATUS_CLR_OFFSET);
323 writel(clear_mask, aio->cygaud->audio + ESR1_STATUS_CLR_OFFSET);
324 writel(clear_mask, aio->cygaud->audio + ESR3_STATUS_CLR_OFFSET);
325
326 writel(clear_mask, aio->cygaud->audio + ESR0_MASK_CLR_OFFSET);
327 writel(clear_mask, aio->cygaud->audio + ESR1_MASK_CLR_OFFSET);
328 writel(clear_mask, aio->cygaud->audio + ESR3_MASK_CLR_OFFSET);
329
330 writel(ANY_PLAYBACK_IRQ,
331 aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
332 } else {
333 writel(clear_mask, aio->cygaud->audio + ESR2_STATUS_CLR_OFFSET);
334 writel(clear_mask, aio->cygaud->audio + ESR4_STATUS_CLR_OFFSET);
335 writel(clear_mask, aio->cygaud->audio + ESR2_MASK_CLR_OFFSET);
336 writel(clear_mask, aio->cygaud->audio + ESR4_MASK_CLR_OFFSET);
337
338 writel(ANY_CAPTURE_IRQ,
339 aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
340 }
341
342}
343
344static void disable_intr(struct snd_pcm_substream *substream)
345{
346 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
347 struct cygnus_aio_port *aio;
348 u32 set_mask;
349
350 aio = cygnus_dai_get_dma_data(substream);
351
352 dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s on port %d\n", __func__, aio->portnum);
353
354
355 set_mask = BIT(aio->portnum);
356
357 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
358
359 writel(set_mask, aio->cygaud->audio + ESR0_MASK_SET_OFFSET);
360 writel(set_mask, aio->cygaud->audio + ESR1_MASK_SET_OFFSET);
361 writel(set_mask, aio->cygaud->audio + ESR3_MASK_SET_OFFSET);
362 } else {
363 writel(set_mask, aio->cygaud->audio + ESR2_MASK_SET_OFFSET);
364 writel(set_mask, aio->cygaud->audio + ESR4_MASK_SET_OFFSET);
365 }
366
367}
368
369static int cygnus_pcm_trigger(struct snd_soc_component *component,
370 struct snd_pcm_substream *substream, int cmd)
371{
372 int ret = 0;
373
374 switch (cmd) {
375 case SNDRV_PCM_TRIGGER_START:
376 case SNDRV_PCM_TRIGGER_RESUME:
377 enable_intr(substream);
378 break;
379
380 case SNDRV_PCM_TRIGGER_STOP:
381 case SNDRV_PCM_TRIGGER_SUSPEND:
382 disable_intr(substream);
383 break;
384 default:
385 ret = -EINVAL;
386 }
387
388 return ret;
389}
390
391static void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream)
392{
393 struct cygnus_aio_port *aio;
394 struct ringbuf_regs *p_rbuf = NULL;
395 u32 regval;
396
397 aio = cygnus_dai_get_dma_data(substream);
398
399 p_rbuf = get_ringbuf(substream);
400
401
402
403
404
405 snd_pcm_period_elapsed(substream);
406
407 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
408
409 regval = readl(aio->cygaud->audio + p_rbuf->rdaddr);
410 regval = regval ^ BIT(31);
411 writel(regval, aio->cygaud->audio + p_rbuf->wraddr);
412 } else {
413
414 regval = readl(aio->cygaud->audio + p_rbuf->wraddr);
415 writel(regval, aio->cygaud->audio + p_rbuf->rdaddr);
416 }
417}
418
419
420
421
422
423
424
425
426static void handle_playback_irq(struct cygnus_audio *cygaud)
427{
428 void __iomem *audio_io;
429 u32 port;
430 u32 esr_status0, esr_status1, esr_status3;
431
432 audio_io = cygaud->audio;
433
434
435
436
437
438
439
440 esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET);
441 esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET);
442 esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET);
443 esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET);
444 esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET);
445 esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET);
446
447 for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) {
448 u32 esrmask = BIT(port);
449
450
451
452
453
454
455
456
457 if ((esrmask & esr_status1) || (esrmask & esr_status0)) {
458 dev_dbg(cygaud->dev,
459 "Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n",
460 esr_status0, esr_status1, esr_status3);
461 }
462
463
464
465
466
467 if (esrmask & esr_status3) {
468 struct snd_pcm_substream *playstr;
469
470 playstr = cygaud->portinfo[port].play_stream;
471 cygnus_pcm_period_elapsed(playstr);
472 }
473 }
474
475
476 writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET);
477 writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET);
478 writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET);
479
480 writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET);
481}
482
483
484
485
486
487
488
489static void handle_capture_irq(struct cygnus_audio *cygaud)
490{
491 void __iomem *audio_io;
492 u32 port;
493 u32 esr_status2, esr_status4;
494
495 audio_io = cygaud->audio;
496
497
498
499
500
501
502
503 esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET);
504 esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET);
505 esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET);
506 esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET);
507
508 for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) {
509 u32 esrmask = BIT(port);
510
511
512
513
514
515
516
517
518 if (esrmask & esr_status2)
519 dev_dbg(cygaud->dev,
520 "Overflow: esr2=0x%x\n", esr_status2);
521
522 if (esrmask & esr_status4) {
523 struct snd_pcm_substream *capstr;
524
525 capstr = cygaud->portinfo[port].capture_stream;
526 cygnus_pcm_period_elapsed(capstr);
527 }
528 }
529
530 writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET);
531 writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET);
532
533 writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET);
534}
535
536static irqreturn_t cygnus_dma_irq(int irq, void *data)
537{
538 u32 r5_status;
539 struct cygnus_audio *cygaud = data;
540
541
542
543
544
545
546
547
548
549 r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET);
550
551 if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ)))
552 return IRQ_NONE;
553
554
555 if (ANY_PLAYBACK_IRQ & r5_status) {
556 handle_playback_irq(cygaud);
557 writel(ANY_PLAYBACK_IRQ & r5_status,
558 cygaud->audio + INTH_R5F_CLEAR_OFFSET);
559 }
560
561
562 if (ANY_CAPTURE_IRQ & r5_status) {
563 handle_capture_irq(cygaud);
564 writel(ANY_CAPTURE_IRQ & r5_status,
565 cygaud->audio + INTH_R5F_CLEAR_OFFSET);
566 }
567
568 return IRQ_HANDLED;
569}
570
571static int cygnus_pcm_open(struct snd_soc_component *component,
572 struct snd_pcm_substream *substream)
573{
574 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
575 struct snd_pcm_runtime *runtime = substream->runtime;
576 struct cygnus_aio_port *aio;
577 int ret;
578
579 aio = cygnus_dai_get_dma_data(substream);
580 if (!aio)
581 return -ENODEV;
582
583 dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
584
585 snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw);
586
587 ret = snd_pcm_hw_constraint_step(runtime, 0,
588 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN);
589 if (ret < 0)
590 return ret;
591
592 ret = snd_pcm_hw_constraint_step(runtime, 0,
593 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN);
594 if (ret < 0)
595 return ret;
596
597
598
599
600 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
601 aio->play_stream = substream;
602 else
603 aio->capture_stream = substream;
604
605 return 0;
606}
607
608static int cygnus_pcm_close(struct snd_soc_component *component,
609 struct snd_pcm_substream *substream)
610{
611 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
612 struct cygnus_aio_port *aio;
613
614 aio = cygnus_dai_get_dma_data(substream);
615
616 dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
617
618 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
619 aio->play_stream = NULL;
620 else
621 aio->capture_stream = NULL;
622
623 if (!aio->play_stream && !aio->capture_stream)
624 dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "freed port %d\n", aio->portnum);
625
626 return 0;
627}
628
629static int cygnus_pcm_prepare(struct snd_soc_component *component,
630 struct snd_pcm_substream *substream)
631{
632 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
633 struct snd_pcm_runtime *runtime = substream->runtime;
634 struct cygnus_aio_port *aio;
635 unsigned long bufsize, periodsize;
636 bool is_play;
637 u32 start;
638 struct ringbuf_regs *p_rbuf = NULL;
639
640 aio = cygnus_dai_get_dma_data(substream);
641 dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
642
643 bufsize = snd_pcm_lib_buffer_bytes(substream);
644 periodsize = snd_pcm_lib_period_bytes(substream);
645
646 dev_dbg(snd_soc_rtd_to_cpu(rtd, 0)->dev, "%s (buf_size %lu) (period_size %lu)\n",
647 __func__, bufsize, periodsize);
648
649 configure_ringbuf_regs(substream);
650
651 p_rbuf = get_ringbuf(substream);
652
653 start = runtime->dma_addr;
654
655 is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
656
657 ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start,
658 periodsize, bufsize);
659
660 return 0;
661}
662
663static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component,
664 struct snd_pcm_substream *substream)
665{
666 struct cygnus_aio_port *aio;
667 unsigned int res = 0, cur = 0, base = 0;
668 struct ringbuf_regs *p_rbuf = NULL;
669
670 aio = cygnus_dai_get_dma_data(substream);
671
672
673
674
675
676 p_rbuf = get_ringbuf(substream);
677 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
678 cur = readl(aio->cygaud->audio + p_rbuf->rdaddr);
679 else
680 cur = readl(aio->cygaud->audio + p_rbuf->wraddr);
681
682 base = readl(aio->cygaud->audio + p_rbuf->baseaddr);
683
684
685
686
687
688 res = (cur & 0x7fffffff) - (base & 0x7fffffff);
689
690 return bytes_to_frames(substream->runtime, res);
691}
692
693static int cygnus_dma_new(struct snd_soc_component *component,
694 struct snd_soc_pcm_runtime *rtd)
695{
696 size_t size = cygnus_pcm_hw.buffer_bytes_max;
697 struct snd_card *card = rtd->card->snd_card;
698
699 if (!card->dev->dma_mask)
700 card->dev->dma_mask = &cygnus_dma_dmamask;
701 if (!card->dev->coherent_dma_mask)
702 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
703
704 snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
705 card->dev, size, size);
706
707 return 0;
708}
709
710static const struct snd_soc_component_driver cygnus_soc_platform = {
711 .open = cygnus_pcm_open,
712 .close = cygnus_pcm_close,
713 .prepare = cygnus_pcm_prepare,
714 .trigger = cygnus_pcm_trigger,
715 .pointer = cygnus_pcm_pointer,
716 .pcm_construct = cygnus_dma_new,
717};
718
719int cygnus_soc_platform_register(struct device *dev,
720 struct cygnus_audio *cygaud)
721{
722 int rc;
723
724 dev_dbg(dev, "%s Enter\n", __func__);
725
726 rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq,
727 IRQF_SHARED, "cygnus-audio", cygaud);
728 if (rc) {
729 dev_err(dev, "%s request_irq error %d\n", __func__, rc);
730 return rc;
731 }
732
733 rc = devm_snd_soc_register_component(dev, &cygnus_soc_platform,
734 NULL, 0);
735 if (rc) {
736 dev_err(dev, "%s failed\n", __func__);
737 return rc;
738 }
739
740 return 0;
741}
742
743int cygnus_soc_platform_unregister(struct device *dev)
744{
745 return 0;
746}
747
748MODULE_LICENSE("GPL v2");
749MODULE_AUTHOR("Broadcom");
750MODULE_DESCRIPTION("Cygnus ASoC PCM module");
751