Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/bcm/cygnus-pcm.c
26428 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
// Copyright (C) 2014-2015 Broadcom Corporation
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
/* Register offset needed for ASoC PCM module */
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
/* Ring Buffer Ctrl Regs --- Start */
29
/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_RDADDR_REG_BASE */
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
/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_WRADDR_REG_BASE */
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
/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_BASEADDR_REG_BASE */
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
/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_ENDADDR_REG_BASE */
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
/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_FREE_MARK_REG_BASE */
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
/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_RDADDR_REG_BASE */
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
/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_WRADDR_REG_BASE */
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
/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_BASEADDR_REG_BASE */
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
/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_ENDADDR_REG_BASE */
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
/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_FULL_MARK_REG_BASE */
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
/* Ring Buffer Ctrl Regs --- End */
114
115
/* Error Status Regs --- Start */
116
/* AUD_FMM_BF_ESR_ESRX_STATUS_REG_BASE */
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
/* AUD_FMM_BF_ESR_ESRX_STATUS_CLEAR_REG_BASE */
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
/* AUD_FMM_BF_ESR_ESRX_MASK_REG_BASE */
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
/* AUD_FMM_BF_ESR_ESRX_MASK_SET_REG_BASE */
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
/* AUD_FMM_BF_ESR_ESRX_MASK_CLEAR_REG_BASE */
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
/* Error Status Regs --- End */
151
152
#define R5F_ESR0_SHIFT 0 /* esr0 = fifo underflow */
153
#define R5F_ESR1_SHIFT 1 /* esr1 = ringbuf underflow */
154
#define R5F_ESR2_SHIFT 2 /* esr2 = ringbuf overflow */
155
#define R5F_ESR3_SHIFT 3 /* esr3 = freemark */
156
#define R5F_ESR4_SHIFT 4 /* esr4 = fullmark */
157
158
159
/* Mask for R5F register. Set all relevant interrupt for playback handler */
160
#define ANY_PLAYBACK_IRQ (BIT(R5F_ESR0_SHIFT) | \
161
BIT(R5F_ESR1_SHIFT) | \
162
BIT(R5F_ESR3_SHIFT))
163
164
/* Mask for R5F register. Set all relevant interrupt for capture handler */
165
#define ANY_CAPTURE_IRQ (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT))
166
167
/*
168
* PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick.
169
* This number should be a multiple of 256. Minimum value is 256
170
*/
171
#define PERIOD_BYTES_MIN 0x100
172
173
static 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
/* A period is basically an interrupt */
181
.period_bytes_min = PERIOD_BYTES_MIN,
182
.period_bytes_max = 0x10000,
183
184
/* period_min/max gives range of approx interrupts per buffer */
185
.periods_min = 2,
186
.periods_max = 8,
187
188
/*
189
* maximum buffer size in bytes = period_bytes_max * periods_max
190
* We allocate this amount of data for each enabled channel
191
*/
192
.buffer_bytes_max = 4 * 0x8000,
193
};
194
195
static u64 cygnus_dma_dmamask = DMA_BIT_MASK(32);
196
197
static 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
205
static 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; /* free or full mark */
216
217
p_rbuf->period_bytes = periodsize;
218
p_rbuf->buf_size = bufsize;
219
220
if (is_playback) {
221
/* Set the pointers to indicate full (flip uppermost bit) */
222
initial_rd = start;
223
initial_wr = initial_rd ^ BIT(31);
224
} else {
225
/* Set the pointers to indicate empty */
226
initial_wr = start;
227
initial_rd = initial_wr;
228
}
229
230
end = start + bufsize - 1;
231
232
/*
233
* The interrupt will fire when free/full mark is *exceeded*
234
* The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark
235
* to be PERIOD_BYTES_MIN less than the period size.
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
246
static 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
/* Map the ssp portnum to a set of ring buffers. */
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: /* SPDIF */
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
295
static 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
310
static 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
/* The port number maps to the bit position to be cleared */
318
clear_mask = BIT(aio->portnum);
319
320
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
321
/* Clear interrupt status before enabling them */
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
/* Unmask the interrupts of the given port*/
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
344
static 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
/* The port number maps to the bit position to be set */
355
set_mask = BIT(aio->portnum);
356
357
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
358
/* Mask the interrupts of the given port*/
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
369
static 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
391
static 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
* If free/full mark interrupt occurs, provide timestamp
403
* to ALSA and update appropriate idx by period_bytes
404
*/
405
snd_pcm_period_elapsed(substream);
406
407
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
408
/* Set the ring buffer to full */
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
/* Set the ring buffer to empty */
414
regval = readl(aio->cygaud->audio + p_rbuf->wraddr);
415
writel(regval, aio->cygaud->audio + p_rbuf->rdaddr);
416
}
417
}
418
419
/*
420
* ESR0/1/3 status Description
421
* 0x1 I2S0_out port caused interrupt
422
* 0x2 I2S1_out port caused interrupt
423
* 0x4 I2S2_out port caused interrupt
424
* 0x8 SPDIF_out port caused interrupt
425
*/
426
static 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
* ESR status gets updates with/without interrupts enabled.
436
* So, check the ESR mask, which provides interrupt enable/
437
* disable status and use it to determine which ESR status
438
* should be serviced.
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
* Ringbuffer or FIFO underflow
452
* If we get this interrupt then, it is also true that we have
453
* not yet responded to the freemark interrupt.
454
* Log a debug message. The freemark handler below will
455
* handle getting everything going again.
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
* Freemark is hit. This is the normal interrupt.
465
* In typical operation the read and write regs will be equal
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
/* Clear ESR interrupt */
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
/* Rearm freemark logic by writing 1 to the correct bit */
480
writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET);
481
}
482
483
/*
484
* ESR2/4 status Description
485
* 0x1 I2S0_in port caused interrupt
486
* 0x2 I2S1_in port caused interrupt
487
* 0x4 I2S2_in port caused interrupt
488
*/
489
static 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
* ESR status gets updates with/without interrupts enabled.
499
* So, check the ESR mask, which provides interrupt enable/
500
* disable status and use it to determine which ESR status
501
* should be serviced.
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
* Ringbuffer or FIFO overflow
513
* If we get this interrupt then, it is also true that we have
514
* not yet responded to the fullmark interrupt.
515
* Log a debug message. The fullmark handler below will
516
* handle getting everything going again.
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
/* Rearm fullmark logic by writing 1 to the correct bit */
533
writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET);
534
}
535
536
static irqreturn_t cygnus_dma_irq(int irq, void *data)
537
{
538
u32 r5_status;
539
struct cygnus_audio *cygaud = data;
540
541
/*
542
* R5 status bits Description
543
* 0 ESR0 (playback FIFO interrupt)
544
* 1 ESR1 (playback rbuf interrupt)
545
* 2 ESR2 (capture rbuf interrupt)
546
* 3 ESR3 (Freemark play. interrupt)
547
* 4 ESR4 (Fullmark capt. interrupt)
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
/* If playback interrupt happened */
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
/* If capture interrupt happened */
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
571
static 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
* Keep track of which substream belongs to which port.
598
* This info is needed by snd_pcm_period_elapsed() in irq_handler
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
608
static 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
629
static 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
663
static 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
* Get the offset of the current read (for playack) or write
674
* index (for capture). Report this value back to the asoc framework.
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
* Mask off the MSB of the rdaddr,wraddr and baseaddr
686
* since MSB is not part of the address
687
*/
688
res = (cur & 0x7fffffff) - (base & 0x7fffffff);
689
690
return bytes_to_frames(substream->runtime, res);
691
}
692
693
static 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
710
static 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
719
int 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
743
int cygnus_soc_platform_unregister(struct device *dev)
744
{
745
return 0;
746
}
747
748
MODULE_LICENSE("GPL v2");
749
MODULE_AUTHOR("Broadcom");
750
MODULE_DESCRIPTION("Cygnus ASoC PCM module");
751
752