File Index
Files and directories
Sign in
FILE

cygnus-pcm.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/bcm/cygnus-pcm.c C
  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
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	/* 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
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; /* 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
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	/* 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
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	/* 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
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	/* 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
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	 * 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 */
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	 * 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 */
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	 * 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
536static 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
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	 * 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
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	 * 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
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");
~/ Read-only file preview.