xref: /openbmc/linux/sound/soc/bcm/cygnus-pcm.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1*2aec85b2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2*2aec85b2SThomas Gleixner // Copyright (C) 2014-2015 Broadcom Corporation
31200a7d9SSimran Rai #include <linux/debugfs.h>
41200a7d9SSimran Rai #include <linux/dma-mapping.h>
51200a7d9SSimran Rai #include <linux/init.h>
61200a7d9SSimran Rai #include <linux/io.h>
71200a7d9SSimran Rai #include <linux/module.h>
81200a7d9SSimran Rai #include <linux/slab.h>
91200a7d9SSimran Rai #include <linux/timer.h>
101200a7d9SSimran Rai #include <sound/core.h>
111200a7d9SSimran Rai #include <sound/pcm.h>
121200a7d9SSimran Rai #include <sound/pcm_params.h>
131200a7d9SSimran Rai #include <sound/soc.h>
141200a7d9SSimran Rai #include <sound/soc-dai.h>
151200a7d9SSimran Rai 
161200a7d9SSimran Rai #include "cygnus-ssp.h"
171200a7d9SSimran Rai 
181200a7d9SSimran Rai /* Register offset needed for ASoC PCM module */
191200a7d9SSimran Rai 
201200a7d9SSimran Rai #define INTH_R5F_STATUS_OFFSET     0x040
211200a7d9SSimran Rai #define INTH_R5F_CLEAR_OFFSET      0x048
221200a7d9SSimran Rai #define INTH_R5F_MASK_SET_OFFSET   0x050
231200a7d9SSimran Rai #define INTH_R5F_MASK_CLEAR_OFFSET 0x054
241200a7d9SSimran Rai 
251200a7d9SSimran Rai #define BF_REARM_FREE_MARK_OFFSET 0x344
261200a7d9SSimran Rai #define BF_REARM_FULL_MARK_OFFSET 0x348
271200a7d9SSimran Rai 
281200a7d9SSimran Rai /* Ring Buffer Ctrl Regs --- Start */
291200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_RDADDR_REG_BASE */
301200a7d9SSimran Rai #define SRC_RBUF_0_RDADDR_OFFSET 0x500
311200a7d9SSimran Rai #define SRC_RBUF_1_RDADDR_OFFSET 0x518
321200a7d9SSimran Rai #define SRC_RBUF_2_RDADDR_OFFSET 0x530
331200a7d9SSimran Rai #define SRC_RBUF_3_RDADDR_OFFSET 0x548
341200a7d9SSimran Rai #define SRC_RBUF_4_RDADDR_OFFSET 0x560
351200a7d9SSimran Rai #define SRC_RBUF_5_RDADDR_OFFSET 0x578
361200a7d9SSimran Rai #define SRC_RBUF_6_RDADDR_OFFSET 0x590
371200a7d9SSimran Rai 
381200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_WRADDR_REG_BASE */
391200a7d9SSimran Rai #define SRC_RBUF_0_WRADDR_OFFSET 0x504
401200a7d9SSimran Rai #define SRC_RBUF_1_WRADDR_OFFSET 0x51c
411200a7d9SSimran Rai #define SRC_RBUF_2_WRADDR_OFFSET 0x534
421200a7d9SSimran Rai #define SRC_RBUF_3_WRADDR_OFFSET 0x54c
431200a7d9SSimran Rai #define SRC_RBUF_4_WRADDR_OFFSET 0x564
441200a7d9SSimran Rai #define SRC_RBUF_5_WRADDR_OFFSET 0x57c
451200a7d9SSimran Rai #define SRC_RBUF_6_WRADDR_OFFSET 0x594
461200a7d9SSimran Rai 
471200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_BASEADDR_REG_BASE */
481200a7d9SSimran Rai #define SRC_RBUF_0_BASEADDR_OFFSET 0x508
491200a7d9SSimran Rai #define SRC_RBUF_1_BASEADDR_OFFSET 0x520
501200a7d9SSimran Rai #define SRC_RBUF_2_BASEADDR_OFFSET 0x538
511200a7d9SSimran Rai #define SRC_RBUF_3_BASEADDR_OFFSET 0x550
521200a7d9SSimran Rai #define SRC_RBUF_4_BASEADDR_OFFSET 0x568
531200a7d9SSimran Rai #define SRC_RBUF_5_BASEADDR_OFFSET 0x580
541200a7d9SSimran Rai #define SRC_RBUF_6_BASEADDR_OFFSET 0x598
551200a7d9SSimran Rai 
561200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_ENDADDR_REG_BASE */
571200a7d9SSimran Rai #define SRC_RBUF_0_ENDADDR_OFFSET 0x50c
581200a7d9SSimran Rai #define SRC_RBUF_1_ENDADDR_OFFSET 0x524
591200a7d9SSimran Rai #define SRC_RBUF_2_ENDADDR_OFFSET 0x53c
601200a7d9SSimran Rai #define SRC_RBUF_3_ENDADDR_OFFSET 0x554
611200a7d9SSimran Rai #define SRC_RBUF_4_ENDADDR_OFFSET 0x56c
621200a7d9SSimran Rai #define SRC_RBUF_5_ENDADDR_OFFSET 0x584
631200a7d9SSimran Rai #define SRC_RBUF_6_ENDADDR_OFFSET 0x59c
641200a7d9SSimran Rai 
651200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_FREE_MARK_REG_BASE */
661200a7d9SSimran Rai #define SRC_RBUF_0_FREE_MARK_OFFSET 0x510
671200a7d9SSimran Rai #define SRC_RBUF_1_FREE_MARK_OFFSET 0x528
681200a7d9SSimran Rai #define SRC_RBUF_2_FREE_MARK_OFFSET 0x540
691200a7d9SSimran Rai #define SRC_RBUF_3_FREE_MARK_OFFSET 0x558
701200a7d9SSimran Rai #define SRC_RBUF_4_FREE_MARK_OFFSET 0x570
711200a7d9SSimran Rai #define SRC_RBUF_5_FREE_MARK_OFFSET 0x588
721200a7d9SSimran Rai #define SRC_RBUF_6_FREE_MARK_OFFSET 0x5a0
731200a7d9SSimran Rai 
741200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_RDADDR_REG_BASE */
751200a7d9SSimran Rai #define DST_RBUF_0_RDADDR_OFFSET 0x5c0
761200a7d9SSimran Rai #define DST_RBUF_1_RDADDR_OFFSET 0x5d8
771200a7d9SSimran Rai #define DST_RBUF_2_RDADDR_OFFSET 0x5f0
781200a7d9SSimran Rai #define DST_RBUF_3_RDADDR_OFFSET 0x608
791200a7d9SSimran Rai #define DST_RBUF_4_RDADDR_OFFSET 0x620
801200a7d9SSimran Rai #define DST_RBUF_5_RDADDR_OFFSET 0x638
811200a7d9SSimran Rai 
821200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_WRADDR_REG_BASE */
831200a7d9SSimran Rai #define DST_RBUF_0_WRADDR_OFFSET 0x5c4
841200a7d9SSimran Rai #define DST_RBUF_1_WRADDR_OFFSET 0x5dc
851200a7d9SSimran Rai #define DST_RBUF_2_WRADDR_OFFSET 0x5f4
861200a7d9SSimran Rai #define DST_RBUF_3_WRADDR_OFFSET 0x60c
871200a7d9SSimran Rai #define DST_RBUF_4_WRADDR_OFFSET 0x624
881200a7d9SSimran Rai #define DST_RBUF_5_WRADDR_OFFSET 0x63c
891200a7d9SSimran Rai 
901200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_BASEADDR_REG_BASE */
911200a7d9SSimran Rai #define DST_RBUF_0_BASEADDR_OFFSET 0x5c8
921200a7d9SSimran Rai #define DST_RBUF_1_BASEADDR_OFFSET 0x5e0
931200a7d9SSimran Rai #define DST_RBUF_2_BASEADDR_OFFSET 0x5f8
941200a7d9SSimran Rai #define DST_RBUF_3_BASEADDR_OFFSET 0x610
951200a7d9SSimran Rai #define DST_RBUF_4_BASEADDR_OFFSET 0x628
961200a7d9SSimran Rai #define DST_RBUF_5_BASEADDR_OFFSET 0x640
971200a7d9SSimran Rai 
981200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_ENDADDR_REG_BASE */
991200a7d9SSimran Rai #define DST_RBUF_0_ENDADDR_OFFSET 0x5cc
1001200a7d9SSimran Rai #define DST_RBUF_1_ENDADDR_OFFSET 0x5e4
1011200a7d9SSimran Rai #define DST_RBUF_2_ENDADDR_OFFSET 0x5fc
1021200a7d9SSimran Rai #define DST_RBUF_3_ENDADDR_OFFSET 0x614
1031200a7d9SSimran Rai #define DST_RBUF_4_ENDADDR_OFFSET 0x62c
1041200a7d9SSimran Rai #define DST_RBUF_5_ENDADDR_OFFSET 0x644
1051200a7d9SSimran Rai 
1061200a7d9SSimran Rai /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_FULL_MARK_REG_BASE */
1071200a7d9SSimran Rai #define DST_RBUF_0_FULL_MARK_OFFSET 0x5d0
1081200a7d9SSimran Rai #define DST_RBUF_1_FULL_MARK_OFFSET 0x5e8
1091200a7d9SSimran Rai #define DST_RBUF_2_FULL_MARK_OFFSET 0x600
1101200a7d9SSimran Rai #define DST_RBUF_3_FULL_MARK_OFFSET 0x618
1111200a7d9SSimran Rai #define DST_RBUF_4_FULL_MARK_OFFSET 0x630
1121200a7d9SSimran Rai #define DST_RBUF_5_FULL_MARK_OFFSET 0x648
1131200a7d9SSimran Rai /* Ring Buffer Ctrl Regs --- End */
1141200a7d9SSimran Rai 
1151200a7d9SSimran Rai /* Error Status Regs --- Start */
1161200a7d9SSimran Rai /* AUD_FMM_BF_ESR_ESRX_STATUS_REG_BASE */
1171200a7d9SSimran Rai #define ESR0_STATUS_OFFSET 0x900
1181200a7d9SSimran Rai #define ESR1_STATUS_OFFSET 0x918
1191200a7d9SSimran Rai #define ESR2_STATUS_OFFSET 0x930
1201200a7d9SSimran Rai #define ESR3_STATUS_OFFSET 0x948
1211200a7d9SSimran Rai #define ESR4_STATUS_OFFSET 0x960
1221200a7d9SSimran Rai 
1231200a7d9SSimran Rai /* AUD_FMM_BF_ESR_ESRX_STATUS_CLEAR_REG_BASE */
1241200a7d9SSimran Rai #define ESR0_STATUS_CLR_OFFSET 0x908
1251200a7d9SSimran Rai #define ESR1_STATUS_CLR_OFFSET 0x920
1261200a7d9SSimran Rai #define ESR2_STATUS_CLR_OFFSET 0x938
1271200a7d9SSimran Rai #define ESR3_STATUS_CLR_OFFSET 0x950
1281200a7d9SSimran Rai #define ESR4_STATUS_CLR_OFFSET 0x968
1291200a7d9SSimran Rai 
1301200a7d9SSimran Rai /* AUD_FMM_BF_ESR_ESRX_MASK_REG_BASE */
1311200a7d9SSimran Rai #define ESR0_MASK_STATUS_OFFSET 0x90c
1321200a7d9SSimran Rai #define ESR1_MASK_STATUS_OFFSET 0x924
1331200a7d9SSimran Rai #define ESR2_MASK_STATUS_OFFSET 0x93c
1341200a7d9SSimran Rai #define ESR3_MASK_STATUS_OFFSET 0x954
1351200a7d9SSimran Rai #define ESR4_MASK_STATUS_OFFSET 0x96c
1361200a7d9SSimran Rai 
1371200a7d9SSimran Rai /* AUD_FMM_BF_ESR_ESRX_MASK_SET_REG_BASE */
1381200a7d9SSimran Rai #define ESR0_MASK_SET_OFFSET 0x910
1391200a7d9SSimran Rai #define ESR1_MASK_SET_OFFSET 0x928
1401200a7d9SSimran Rai #define ESR2_MASK_SET_OFFSET 0x940
1411200a7d9SSimran Rai #define ESR3_MASK_SET_OFFSET 0x958
1421200a7d9SSimran Rai #define ESR4_MASK_SET_OFFSET 0x970
1431200a7d9SSimran Rai 
1441200a7d9SSimran Rai /* AUD_FMM_BF_ESR_ESRX_MASK_CLEAR_REG_BASE */
1451200a7d9SSimran Rai #define ESR0_MASK_CLR_OFFSET 0x914
1461200a7d9SSimran Rai #define ESR1_MASK_CLR_OFFSET 0x92c
1471200a7d9SSimran Rai #define ESR2_MASK_CLR_OFFSET 0x944
1481200a7d9SSimran Rai #define ESR3_MASK_CLR_OFFSET 0x95c
1491200a7d9SSimran Rai #define ESR4_MASK_CLR_OFFSET 0x974
1501200a7d9SSimran Rai /* Error Status Regs --- End */
1511200a7d9SSimran Rai 
1521200a7d9SSimran Rai #define R5F_ESR0_SHIFT  0    /* esr0 = fifo underflow */
1531200a7d9SSimran Rai #define R5F_ESR1_SHIFT  1    /* esr1 = ringbuf underflow */
1541200a7d9SSimran Rai #define R5F_ESR2_SHIFT  2    /* esr2 = ringbuf overflow */
1551200a7d9SSimran Rai #define R5F_ESR3_SHIFT  3    /* esr3 = freemark */
1561200a7d9SSimran Rai #define R5F_ESR4_SHIFT  4    /* esr4 = fullmark */
1571200a7d9SSimran Rai 
1581200a7d9SSimran Rai 
1591200a7d9SSimran Rai /* Mask for R5F register.  Set all relevant interrupt for playback handler */
1601200a7d9SSimran Rai #define ANY_PLAYBACK_IRQ  (BIT(R5F_ESR0_SHIFT) | \
1611200a7d9SSimran Rai 			   BIT(R5F_ESR1_SHIFT) | \
1621200a7d9SSimran Rai 			   BIT(R5F_ESR3_SHIFT))
1631200a7d9SSimran Rai 
1641200a7d9SSimran Rai /* Mask for R5F register.  Set all relevant interrupt for capture handler */
1651200a7d9SSimran Rai #define ANY_CAPTURE_IRQ   (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT))
1661200a7d9SSimran Rai 
1671200a7d9SSimran Rai /*
1681200a7d9SSimran Rai  * PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick.
1691200a7d9SSimran Rai  * This number should be a multiple of 256. Minimum value is 256
1701200a7d9SSimran Rai  */
1711200a7d9SSimran Rai #define PERIOD_BYTES_MIN 0x100
1721200a7d9SSimran Rai 
1731200a7d9SSimran Rai static const struct snd_pcm_hardware cygnus_pcm_hw = {
1741200a7d9SSimran Rai 	.info = SNDRV_PCM_INFO_MMAP |
1751200a7d9SSimran Rai 			SNDRV_PCM_INFO_MMAP_VALID |
1761200a7d9SSimran Rai 			SNDRV_PCM_INFO_INTERLEAVED,
1771200a7d9SSimran Rai 	.formats = SNDRV_PCM_FMTBIT_S16_LE |
1781200a7d9SSimran Rai 			SNDRV_PCM_FMTBIT_S32_LE,
1791200a7d9SSimran Rai 
1801200a7d9SSimran Rai 	/* A period is basically an interrupt */
1811200a7d9SSimran Rai 	.period_bytes_min = PERIOD_BYTES_MIN,
1821200a7d9SSimran Rai 	.period_bytes_max = 0x10000,
1831200a7d9SSimran Rai 
1841200a7d9SSimran Rai 	/* period_min/max gives range of approx interrupts per buffer */
1851200a7d9SSimran Rai 	.periods_min = 2,
1861200a7d9SSimran Rai 	.periods_max = 8,
1871200a7d9SSimran Rai 
1881200a7d9SSimran Rai 	/*
1891200a7d9SSimran Rai 	 * maximum buffer size in bytes = period_bytes_max * periods_max
1901200a7d9SSimran Rai 	 * We allocate this amount of data for each enabled channel
1911200a7d9SSimran Rai 	 */
1921200a7d9SSimran Rai 	.buffer_bytes_max = 4 * 0x8000,
1931200a7d9SSimran Rai };
1941200a7d9SSimran Rai 
1951200a7d9SSimran Rai static u64 cygnus_dma_dmamask = DMA_BIT_MASK(32);
1961200a7d9SSimran Rai 
cygnus_dai_get_dma_data(struct snd_pcm_substream * substream)1971200a7d9SSimran Rai static struct cygnus_aio_port *cygnus_dai_get_dma_data(
1981200a7d9SSimran Rai 				struct snd_pcm_substream *substream)
1991200a7d9SSimran Rai {
2000359c834SKuninori Morimoto 	struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
2011200a7d9SSimran Rai 
202fc392364SKuninori Morimoto 	return snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(soc_runtime, 0), substream);
2031200a7d9SSimran Rai }
2041200a7d9SSimran Rai 
ringbuf_set_initial(void __iomem * audio_io,struct ringbuf_regs * p_rbuf,bool is_playback,u32 start,u32 periodsize,u32 bufsize)2051200a7d9SSimran Rai static void ringbuf_set_initial(void __iomem *audio_io,
2061200a7d9SSimran Rai 		struct ringbuf_regs *p_rbuf,
2071200a7d9SSimran Rai 		bool is_playback,
2081200a7d9SSimran Rai 		u32 start,
2091200a7d9SSimran Rai 		u32 periodsize,
2101200a7d9SSimran Rai 		u32 bufsize)
2111200a7d9SSimran Rai {
2121200a7d9SSimran Rai 	u32 initial_rd;
2131200a7d9SSimran Rai 	u32 initial_wr;
2141200a7d9SSimran Rai 	u32 end;
2151200a7d9SSimran Rai 	u32 fmark_val; /* free or full mark */
2161200a7d9SSimran Rai 
2171200a7d9SSimran Rai 	p_rbuf->period_bytes = periodsize;
2181200a7d9SSimran Rai 	p_rbuf->buf_size = bufsize;
2191200a7d9SSimran Rai 
2201200a7d9SSimran Rai 	if (is_playback) {
2211200a7d9SSimran Rai 		/* Set the pointers to indicate full (flip uppermost bit) */
2221200a7d9SSimran Rai 		initial_rd = start;
2231200a7d9SSimran Rai 		initial_wr = initial_rd ^ BIT(31);
2241200a7d9SSimran Rai 	} else {
2251200a7d9SSimran Rai 		/* Set the pointers to indicate empty */
2261200a7d9SSimran Rai 		initial_wr = start;
2271200a7d9SSimran Rai 		initial_rd = initial_wr;
2281200a7d9SSimran Rai 	}
2291200a7d9SSimran Rai 
2301200a7d9SSimran Rai 	end = start + bufsize - 1;
2311200a7d9SSimran Rai 
2321200a7d9SSimran Rai 	/*
2331200a7d9SSimran Rai 	 * The interrupt will fire when free/full mark is *exceeded*
2341200a7d9SSimran Rai 	 * The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark
2351200a7d9SSimran Rai 	 * to be PERIOD_BYTES_MIN less than the period size.
2361200a7d9SSimran Rai 	 */
2371200a7d9SSimran Rai 	fmark_val = periodsize - PERIOD_BYTES_MIN;
2381200a7d9SSimran Rai 
2391200a7d9SSimran Rai 	writel(start, audio_io + p_rbuf->baseaddr);
2401200a7d9SSimran Rai 	writel(end, audio_io + p_rbuf->endaddr);
2411200a7d9SSimran Rai 	writel(fmark_val, audio_io + p_rbuf->fmark);
2421200a7d9SSimran Rai 	writel(initial_rd, audio_io + p_rbuf->rdaddr);
2431200a7d9SSimran Rai 	writel(initial_wr, audio_io + p_rbuf->wraddr);
2441200a7d9SSimran Rai }
2451200a7d9SSimran Rai 
configure_ringbuf_regs(struct snd_pcm_substream * substream)2461200a7d9SSimran Rai static int configure_ringbuf_regs(struct snd_pcm_substream *substream)
2471200a7d9SSimran Rai {
2481200a7d9SSimran Rai 	struct cygnus_aio_port *aio;
2491200a7d9SSimran Rai 	struct ringbuf_regs *p_rbuf;
2501200a7d9SSimran Rai 	int status = 0;
2511200a7d9SSimran Rai 
2521200a7d9SSimran Rai 	aio = cygnus_dai_get_dma_data(substream);
2531200a7d9SSimran Rai 
2541200a7d9SSimran Rai 	/* Map the ssp portnum to a set of ring buffers. */
2551200a7d9SSimran Rai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
2561200a7d9SSimran Rai 		p_rbuf = &aio->play_rb_regs;
2571200a7d9SSimran Rai 
2581200a7d9SSimran Rai 		switch (aio->portnum) {
2591200a7d9SSimran Rai 		case 0:
2601200a7d9SSimran Rai 			*p_rbuf = RINGBUF_REG_PLAYBACK(0);
2611200a7d9SSimran Rai 			break;
2621200a7d9SSimran Rai 		case 1:
2631200a7d9SSimran Rai 			*p_rbuf = RINGBUF_REG_PLAYBACK(2);
2641200a7d9SSimran Rai 			break;
2651200a7d9SSimran Rai 		case 2:
2661200a7d9SSimran Rai 			*p_rbuf = RINGBUF_REG_PLAYBACK(4);
2671200a7d9SSimran Rai 			break;
2681200a7d9SSimran Rai 		case 3: /* SPDIF */
2691200a7d9SSimran Rai 			*p_rbuf = RINGBUF_REG_PLAYBACK(6);
2701200a7d9SSimran Rai 			break;
2711200a7d9SSimran Rai 		default:
2721200a7d9SSimran Rai 			status = -EINVAL;
2731200a7d9SSimran Rai 		}
2741200a7d9SSimran Rai 	} else {
2751200a7d9SSimran Rai 		p_rbuf = &aio->capture_rb_regs;
2761200a7d9SSimran Rai 
2771200a7d9SSimran Rai 		switch (aio->portnum) {
2781200a7d9SSimran Rai 		case 0:
2791200a7d9SSimran Rai 			*p_rbuf = RINGBUF_REG_CAPTURE(0);
2801200a7d9SSimran Rai 			break;
2811200a7d9SSimran Rai 		case 1:
2821200a7d9SSimran Rai 			*p_rbuf = RINGBUF_REG_CAPTURE(2);
2831200a7d9SSimran Rai 			break;
2841200a7d9SSimran Rai 		case 2:
2851200a7d9SSimran Rai 			*p_rbuf = RINGBUF_REG_CAPTURE(4);
2861200a7d9SSimran Rai 			break;
2871200a7d9SSimran Rai 		default:
2881200a7d9SSimran Rai 			status = -EINVAL;
2891200a7d9SSimran Rai 		}
2901200a7d9SSimran Rai 	}
2911200a7d9SSimran Rai 
2921200a7d9SSimran Rai 	return status;
2931200a7d9SSimran Rai }
2941200a7d9SSimran Rai 
get_ringbuf(struct snd_pcm_substream * substream)2951200a7d9SSimran Rai static struct ringbuf_regs *get_ringbuf(struct snd_pcm_substream *substream)
2961200a7d9SSimran Rai {
2971200a7d9SSimran Rai 	struct cygnus_aio_port *aio;
2981200a7d9SSimran Rai 	struct ringbuf_regs *p_rbuf = NULL;
2991200a7d9SSimran Rai 
3001200a7d9SSimran Rai 	aio = cygnus_dai_get_dma_data(substream);
3011200a7d9SSimran Rai 
3021200a7d9SSimran Rai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
3031200a7d9SSimran Rai 		p_rbuf = &aio->play_rb_regs;
3041200a7d9SSimran Rai 	else
3051200a7d9SSimran Rai 		p_rbuf = &aio->capture_rb_regs;
3061200a7d9SSimran Rai 
3071200a7d9SSimran Rai 	return p_rbuf;
3081200a7d9SSimran Rai }
3091200a7d9SSimran Rai 
enable_intr(struct snd_pcm_substream * substream)3101200a7d9SSimran Rai static void enable_intr(struct snd_pcm_substream *substream)
3111200a7d9SSimran Rai {
3121200a7d9SSimran Rai 	struct cygnus_aio_port *aio;
3131200a7d9SSimran Rai 	u32 clear_mask;
3141200a7d9SSimran Rai 
3151200a7d9SSimran Rai 	aio = cygnus_dai_get_dma_data(substream);
3161200a7d9SSimran Rai 
3171200a7d9SSimran Rai 	/* The port number maps to the bit position to be cleared */
3181200a7d9SSimran Rai 	clear_mask = BIT(aio->portnum);
3191200a7d9SSimran Rai 
3201200a7d9SSimran Rai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
3211200a7d9SSimran Rai 		/* Clear interrupt status before enabling them */
3221200a7d9SSimran Rai 		writel(clear_mask, aio->cygaud->audio + ESR0_STATUS_CLR_OFFSET);
3231200a7d9SSimran Rai 		writel(clear_mask, aio->cygaud->audio + ESR1_STATUS_CLR_OFFSET);
3241200a7d9SSimran Rai 		writel(clear_mask, aio->cygaud->audio + ESR3_STATUS_CLR_OFFSET);
3251200a7d9SSimran Rai 		/* Unmask the interrupts of the given port*/
3261200a7d9SSimran Rai 		writel(clear_mask, aio->cygaud->audio + ESR0_MASK_CLR_OFFSET);
3271200a7d9SSimran Rai 		writel(clear_mask, aio->cygaud->audio + ESR1_MASK_CLR_OFFSET);
3281200a7d9SSimran Rai 		writel(clear_mask, aio->cygaud->audio + ESR3_MASK_CLR_OFFSET);
3291200a7d9SSimran Rai 
3301200a7d9SSimran Rai 		writel(ANY_PLAYBACK_IRQ,
3311200a7d9SSimran Rai 			aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
3321200a7d9SSimran Rai 	} else {
3331200a7d9SSimran Rai 		writel(clear_mask, aio->cygaud->audio + ESR2_STATUS_CLR_OFFSET);
3341200a7d9SSimran Rai 		writel(clear_mask, aio->cygaud->audio + ESR4_STATUS_CLR_OFFSET);
3351200a7d9SSimran Rai 		writel(clear_mask, aio->cygaud->audio + ESR2_MASK_CLR_OFFSET);
3361200a7d9SSimran Rai 		writel(clear_mask, aio->cygaud->audio + ESR4_MASK_CLR_OFFSET);
3371200a7d9SSimran Rai 
3381200a7d9SSimran Rai 		writel(ANY_CAPTURE_IRQ,
3391200a7d9SSimran Rai 			aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
3401200a7d9SSimran Rai 	}
3411200a7d9SSimran Rai 
3421200a7d9SSimran Rai }
3431200a7d9SSimran Rai 
disable_intr(struct snd_pcm_substream * substream)3441200a7d9SSimran Rai static void disable_intr(struct snd_pcm_substream *substream)
3451200a7d9SSimran Rai {
3460359c834SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
3471200a7d9SSimran Rai 	struct cygnus_aio_port *aio;
3481200a7d9SSimran Rai 	u32 set_mask;
3491200a7d9SSimran Rai 
3501200a7d9SSimran Rai 	aio = cygnus_dai_get_dma_data(substream);
3511200a7d9SSimran Rai 
352fc392364SKuninori Morimoto 	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s on port %d\n", __func__, aio->portnum);
3531200a7d9SSimran Rai 
3541200a7d9SSimran Rai 	/* The port number maps to the bit position to be set */
3551200a7d9SSimran Rai 	set_mask = BIT(aio->portnum);
3561200a7d9SSimran Rai 
3571200a7d9SSimran Rai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
3581200a7d9SSimran Rai 		/* Mask the interrupts of the given port*/
3591200a7d9SSimran Rai 		writel(set_mask, aio->cygaud->audio + ESR0_MASK_SET_OFFSET);
3601200a7d9SSimran Rai 		writel(set_mask, aio->cygaud->audio + ESR1_MASK_SET_OFFSET);
3611200a7d9SSimran Rai 		writel(set_mask, aio->cygaud->audio + ESR3_MASK_SET_OFFSET);
3621200a7d9SSimran Rai 	} else {
3631200a7d9SSimran Rai 		writel(set_mask, aio->cygaud->audio + ESR2_MASK_SET_OFFSET);
3641200a7d9SSimran Rai 		writel(set_mask, aio->cygaud->audio + ESR4_MASK_SET_OFFSET);
3651200a7d9SSimran Rai 	}
3661200a7d9SSimran Rai 
3671200a7d9SSimran Rai }
3681200a7d9SSimran Rai 
cygnus_pcm_trigger(struct snd_soc_component * component,struct snd_pcm_substream * substream,int cmd)369dc06bd0fSKuninori Morimoto static int cygnus_pcm_trigger(struct snd_soc_component *component,
370dc06bd0fSKuninori Morimoto 			      struct snd_pcm_substream *substream, int cmd)
3711200a7d9SSimran Rai {
3721200a7d9SSimran Rai 	int ret = 0;
3731200a7d9SSimran Rai 
3741200a7d9SSimran Rai 	switch (cmd) {
3751200a7d9SSimran Rai 	case SNDRV_PCM_TRIGGER_START:
3761200a7d9SSimran Rai 	case SNDRV_PCM_TRIGGER_RESUME:
3771200a7d9SSimran Rai 		enable_intr(substream);
3781200a7d9SSimran Rai 		break;
3791200a7d9SSimran Rai 
3801200a7d9SSimran Rai 	case SNDRV_PCM_TRIGGER_STOP:
3811200a7d9SSimran Rai 	case SNDRV_PCM_TRIGGER_SUSPEND:
3821200a7d9SSimran Rai 		disable_intr(substream);
3831200a7d9SSimran Rai 		break;
3841200a7d9SSimran Rai 	default:
3851200a7d9SSimran Rai 		ret = -EINVAL;
3861200a7d9SSimran Rai 	}
3871200a7d9SSimran Rai 
3881200a7d9SSimran Rai 	return ret;
3891200a7d9SSimran Rai }
3901200a7d9SSimran Rai 
cygnus_pcm_period_elapsed(struct snd_pcm_substream * substream)3911200a7d9SSimran Rai static void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream)
3921200a7d9SSimran Rai {
3931200a7d9SSimran Rai 	struct cygnus_aio_port *aio;
3941200a7d9SSimran Rai 	struct ringbuf_regs *p_rbuf = NULL;
3951200a7d9SSimran Rai 	u32 regval;
3961200a7d9SSimran Rai 
3971200a7d9SSimran Rai 	aio = cygnus_dai_get_dma_data(substream);
3981200a7d9SSimran Rai 
3991200a7d9SSimran Rai 	p_rbuf = get_ringbuf(substream);
4001200a7d9SSimran Rai 
4011200a7d9SSimran Rai 	/*
4021200a7d9SSimran Rai 	 * If free/full mark interrupt occurs, provide timestamp
4031200a7d9SSimran Rai 	 * to ALSA and update appropriate idx by period_bytes
4041200a7d9SSimran Rai 	 */
4051200a7d9SSimran Rai 	snd_pcm_period_elapsed(substream);
4061200a7d9SSimran Rai 
4071200a7d9SSimran Rai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
4081200a7d9SSimran Rai 		/* Set the ring buffer to full */
4091200a7d9SSimran Rai 		regval = readl(aio->cygaud->audio + p_rbuf->rdaddr);
4101200a7d9SSimran Rai 		regval = regval ^ BIT(31);
4111200a7d9SSimran Rai 		writel(regval, aio->cygaud->audio + p_rbuf->wraddr);
4121200a7d9SSimran Rai 	} else {
4131200a7d9SSimran Rai 		/* Set the ring buffer to empty */
4141200a7d9SSimran Rai 		regval = readl(aio->cygaud->audio + p_rbuf->wraddr);
4151200a7d9SSimran Rai 		writel(regval, aio->cygaud->audio + p_rbuf->rdaddr);
4161200a7d9SSimran Rai 	}
4171200a7d9SSimran Rai }
4181200a7d9SSimran Rai 
4191200a7d9SSimran Rai /*
4201200a7d9SSimran Rai  * ESR0/1/3 status  Description
4211200a7d9SSimran Rai  *  0x1	I2S0_out port caused interrupt
4221200a7d9SSimran Rai  *  0x2	I2S1_out port caused interrupt
4231200a7d9SSimran Rai  *  0x4	I2S2_out port caused interrupt
4241200a7d9SSimran Rai  *  0x8	SPDIF_out port caused interrupt
4251200a7d9SSimran Rai  */
handle_playback_irq(struct cygnus_audio * cygaud)4261200a7d9SSimran Rai static void handle_playback_irq(struct cygnus_audio *cygaud)
4271200a7d9SSimran Rai {
4281200a7d9SSimran Rai 	void __iomem *audio_io;
4291200a7d9SSimran Rai 	u32 port;
4301200a7d9SSimran Rai 	u32 esr_status0, esr_status1, esr_status3;
4311200a7d9SSimran Rai 
4321200a7d9SSimran Rai 	audio_io = cygaud->audio;
4331200a7d9SSimran Rai 
4341200a7d9SSimran Rai 	/*
4351200a7d9SSimran Rai 	 * ESR status gets updates with/without interrupts enabled.
4361200a7d9SSimran Rai 	 * So, check the ESR mask, which provides interrupt enable/
4371200a7d9SSimran Rai 	 * disable status and use it to determine which ESR status
4381200a7d9SSimran Rai 	 * should be serviced.
4391200a7d9SSimran Rai 	 */
4401200a7d9SSimran Rai 	esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET);
4411200a7d9SSimran Rai 	esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET);
4421200a7d9SSimran Rai 	esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET);
4431200a7d9SSimran Rai 	esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET);
4441200a7d9SSimran Rai 	esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET);
4451200a7d9SSimran Rai 	esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET);
4461200a7d9SSimran Rai 
4471200a7d9SSimran Rai 	for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) {
4481200a7d9SSimran Rai 		u32 esrmask = BIT(port);
4491200a7d9SSimran Rai 
4501200a7d9SSimran Rai 		/*
4511200a7d9SSimran Rai 		 * Ringbuffer or FIFO underflow
4521200a7d9SSimran Rai 		 * If we get this interrupt then, it is also true that we have
4531200a7d9SSimran Rai 		 * not yet responded to the freemark interrupt.
4541200a7d9SSimran Rai 		 * Log a debug message.  The freemark handler below will
4551200a7d9SSimran Rai 		 * handle getting everything going again.
4561200a7d9SSimran Rai 		 */
4571200a7d9SSimran Rai 		if ((esrmask & esr_status1) || (esrmask & esr_status0)) {
4581200a7d9SSimran Rai 			dev_dbg(cygaud->dev,
4591200a7d9SSimran Rai 				"Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n",
4601200a7d9SSimran Rai 				esr_status0, esr_status1, esr_status3);
4611200a7d9SSimran Rai 		}
4621200a7d9SSimran Rai 
4631200a7d9SSimran Rai 		/*
4641200a7d9SSimran Rai 		 * Freemark is hit. This is the normal interrupt.
4651200a7d9SSimran Rai 		 * In typical operation the read and write regs will be equal
4661200a7d9SSimran Rai 		 */
4671200a7d9SSimran Rai 		if (esrmask & esr_status3) {
4681200a7d9SSimran Rai 			struct snd_pcm_substream *playstr;
4691200a7d9SSimran Rai 
4701200a7d9SSimran Rai 			playstr = cygaud->portinfo[port].play_stream;
4711200a7d9SSimran Rai 			cygnus_pcm_period_elapsed(playstr);
4721200a7d9SSimran Rai 		}
4731200a7d9SSimran Rai 	}
4741200a7d9SSimran Rai 
4751200a7d9SSimran Rai 	/* Clear ESR interrupt */
4761200a7d9SSimran Rai 	writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET);
4771200a7d9SSimran Rai 	writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET);
4781200a7d9SSimran Rai 	writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET);
4791200a7d9SSimran Rai 	/* Rearm freemark logic by writing 1 to the correct bit */
4801200a7d9SSimran Rai 	writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET);
4811200a7d9SSimran Rai }
4821200a7d9SSimran Rai 
4831200a7d9SSimran Rai /*
4841200a7d9SSimran Rai  * ESR2/4 status  Description
4851200a7d9SSimran Rai  *  0x1	I2S0_in port caused interrupt
4861200a7d9SSimran Rai  *  0x2	I2S1_in port caused interrupt
4871200a7d9SSimran Rai  *  0x4	I2S2_in port caused interrupt
4881200a7d9SSimran Rai  */
handle_capture_irq(struct cygnus_audio * cygaud)4891200a7d9SSimran Rai static void handle_capture_irq(struct cygnus_audio *cygaud)
4901200a7d9SSimran Rai {
4911200a7d9SSimran Rai 	void __iomem *audio_io;
4921200a7d9SSimran Rai 	u32 port;
4931200a7d9SSimran Rai 	u32 esr_status2, esr_status4;
4941200a7d9SSimran Rai 
4951200a7d9SSimran Rai 	audio_io = cygaud->audio;
4961200a7d9SSimran Rai 
4971200a7d9SSimran Rai 	/*
4981200a7d9SSimran Rai 	 * ESR status gets updates with/without interrupts enabled.
4991200a7d9SSimran Rai 	 * So, check the ESR mask, which provides interrupt enable/
5001200a7d9SSimran Rai 	 * disable status and use it to determine which ESR status
5011200a7d9SSimran Rai 	 * should be serviced.
5021200a7d9SSimran Rai 	 */
5031200a7d9SSimran Rai 	esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET);
5041200a7d9SSimran Rai 	esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET);
5051200a7d9SSimran Rai 	esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET);
5061200a7d9SSimran Rai 	esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET);
5071200a7d9SSimran Rai 
5081200a7d9SSimran Rai 	for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) {
5091200a7d9SSimran Rai 		u32 esrmask = BIT(port);
5101200a7d9SSimran Rai 
5111200a7d9SSimran Rai 		/*
5121200a7d9SSimran Rai 		 * Ringbuffer or FIFO overflow
5131200a7d9SSimran Rai 		 * If we get this interrupt then, it is also true that we have
5141200a7d9SSimran Rai 		 * not yet responded to the fullmark interrupt.
5151200a7d9SSimran Rai 		 * Log a debug message.  The fullmark handler below will
5161200a7d9SSimran Rai 		 * handle getting everything going again.
5171200a7d9SSimran Rai 		 */
5181200a7d9SSimran Rai 		if (esrmask & esr_status2)
5191200a7d9SSimran Rai 			dev_dbg(cygaud->dev,
5201200a7d9SSimran Rai 				"Overflow: esr2=0x%x\n", esr_status2);
5211200a7d9SSimran Rai 
5221200a7d9SSimran Rai 		if (esrmask & esr_status4) {
5231200a7d9SSimran Rai 			struct snd_pcm_substream *capstr;
5241200a7d9SSimran Rai 
5251200a7d9SSimran Rai 			capstr = cygaud->portinfo[port].capture_stream;
5261200a7d9SSimran Rai 			cygnus_pcm_period_elapsed(capstr);
5271200a7d9SSimran Rai 		}
5281200a7d9SSimran Rai 	}
5291200a7d9SSimran Rai 
5301200a7d9SSimran Rai 	writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET);
5311200a7d9SSimran Rai 	writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET);
5321200a7d9SSimran Rai 	/* Rearm fullmark logic by writing 1 to the correct bit */
5331200a7d9SSimran Rai 	writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET);
5341200a7d9SSimran Rai }
5351200a7d9SSimran Rai 
cygnus_dma_irq(int irq,void * data)5361200a7d9SSimran Rai static irqreturn_t cygnus_dma_irq(int irq, void *data)
5371200a7d9SSimran Rai {
5381200a7d9SSimran Rai 	u32 r5_status;
5391200a7d9SSimran Rai 	struct cygnus_audio *cygaud = data;
5401200a7d9SSimran Rai 
5411200a7d9SSimran Rai 	/*
5421200a7d9SSimran Rai 	 * R5 status bits	Description
5431200a7d9SSimran Rai 	 *  0		ESR0 (playback FIFO interrupt)
5441200a7d9SSimran Rai 	 *  1		ESR1 (playback rbuf interrupt)
5451200a7d9SSimran Rai 	 *  2		ESR2 (capture rbuf interrupt)
5461200a7d9SSimran Rai 	 *  3		ESR3 (Freemark play. interrupt)
5471200a7d9SSimran Rai 	 *  4		ESR4 (Fullmark capt. interrupt)
5481200a7d9SSimran Rai 	 */
5491200a7d9SSimran Rai 	r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET);
5501200a7d9SSimran Rai 
5511200a7d9SSimran Rai 	if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ)))
5521200a7d9SSimran Rai 		return IRQ_NONE;
5531200a7d9SSimran Rai 
5541200a7d9SSimran Rai 	/* If playback interrupt happened */
5551200a7d9SSimran Rai 	if (ANY_PLAYBACK_IRQ & r5_status) {
5561200a7d9SSimran Rai 		handle_playback_irq(cygaud);
5571200a7d9SSimran Rai 		writel(ANY_PLAYBACK_IRQ & r5_status,
5581200a7d9SSimran Rai 			cygaud->audio + INTH_R5F_CLEAR_OFFSET);
5591200a7d9SSimran Rai 	}
5601200a7d9SSimran Rai 
5611200a7d9SSimran Rai 	/* If  capture interrupt happened */
5621200a7d9SSimran Rai 	if (ANY_CAPTURE_IRQ & r5_status) {
5631200a7d9SSimran Rai 		handle_capture_irq(cygaud);
5641200a7d9SSimran Rai 		writel(ANY_CAPTURE_IRQ & r5_status,
5651200a7d9SSimran Rai 			cygaud->audio + INTH_R5F_CLEAR_OFFSET);
5661200a7d9SSimran Rai 	}
5671200a7d9SSimran Rai 
5681200a7d9SSimran Rai 	return IRQ_HANDLED;
5691200a7d9SSimran Rai }
5701200a7d9SSimran Rai 
cygnus_pcm_open(struct snd_soc_component * component,struct snd_pcm_substream * substream)571dc06bd0fSKuninori Morimoto static int cygnus_pcm_open(struct snd_soc_component *component,
572dc06bd0fSKuninori Morimoto 			   struct snd_pcm_substream *substream)
5731200a7d9SSimran Rai {
5740359c834SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
5751200a7d9SSimran Rai 	struct snd_pcm_runtime *runtime = substream->runtime;
5761200a7d9SSimran Rai 	struct cygnus_aio_port *aio;
5771200a7d9SSimran Rai 	int ret;
5781200a7d9SSimran Rai 
5791200a7d9SSimran Rai 	aio = cygnus_dai_get_dma_data(substream);
5801200a7d9SSimran Rai 	if (!aio)
5811200a7d9SSimran Rai 		return -ENODEV;
5821200a7d9SSimran Rai 
583fc392364SKuninori Morimoto 	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
5841200a7d9SSimran Rai 
5851200a7d9SSimran Rai 	snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw);
5861200a7d9SSimran Rai 
5871200a7d9SSimran Rai 	ret = snd_pcm_hw_constraint_step(runtime, 0,
5881200a7d9SSimran Rai 		SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN);
5891200a7d9SSimran Rai 	if (ret < 0)
5901200a7d9SSimran Rai 		return ret;
5911200a7d9SSimran Rai 
5921200a7d9SSimran Rai 	ret = snd_pcm_hw_constraint_step(runtime, 0,
5931200a7d9SSimran Rai 		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN);
5941200a7d9SSimran Rai 	if (ret < 0)
5951200a7d9SSimran Rai 		return ret;
5961200a7d9SSimran Rai 	/*
5971200a7d9SSimran Rai 	 * Keep track of which substream belongs to which port.
5981200a7d9SSimran Rai 	 * This info is needed by snd_pcm_period_elapsed() in irq_handler
5991200a7d9SSimran Rai 	 */
6001200a7d9SSimran Rai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
6011200a7d9SSimran Rai 		aio->play_stream = substream;
6021200a7d9SSimran Rai 	else
6031200a7d9SSimran Rai 		aio->capture_stream = substream;
6041200a7d9SSimran Rai 
6051200a7d9SSimran Rai 	return 0;
6061200a7d9SSimran Rai }
6071200a7d9SSimran Rai 
cygnus_pcm_close(struct snd_soc_component * component,struct snd_pcm_substream * substream)608dc06bd0fSKuninori Morimoto static int cygnus_pcm_close(struct snd_soc_component *component,
609dc06bd0fSKuninori Morimoto 			    struct snd_pcm_substream *substream)
6101200a7d9SSimran Rai {
6110359c834SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
6121200a7d9SSimran Rai 	struct cygnus_aio_port *aio;
6131200a7d9SSimran Rai 
6141200a7d9SSimran Rai 	aio = cygnus_dai_get_dma_data(substream);
6151200a7d9SSimran Rai 
616fc392364SKuninori Morimoto 	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s  port %d\n", __func__, aio->portnum);
6171200a7d9SSimran Rai 
6181200a7d9SSimran Rai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
6191200a7d9SSimran Rai 		aio->play_stream = NULL;
6201200a7d9SSimran Rai 	else
6211200a7d9SSimran Rai 		aio->capture_stream = NULL;
6221200a7d9SSimran Rai 
6231200a7d9SSimran Rai 	if (!aio->play_stream && !aio->capture_stream)
624fc392364SKuninori Morimoto 		dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "freed  port %d\n", aio->portnum);
6251200a7d9SSimran Rai 
6261200a7d9SSimran Rai 	return 0;
6271200a7d9SSimran Rai }
6281200a7d9SSimran Rai 
cygnus_pcm_prepare(struct snd_soc_component * component,struct snd_pcm_substream * substream)629dc06bd0fSKuninori Morimoto static int cygnus_pcm_prepare(struct snd_soc_component *component,
630dc06bd0fSKuninori Morimoto 			      struct snd_pcm_substream *substream)
6311200a7d9SSimran Rai {
6320359c834SKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
6331200a7d9SSimran Rai 	struct snd_pcm_runtime *runtime = substream->runtime;
6341200a7d9SSimran Rai 	struct cygnus_aio_port *aio;
6351200a7d9SSimran Rai 	unsigned long bufsize, periodsize;
6361200a7d9SSimran Rai 	bool is_play;
6371200a7d9SSimran Rai 	u32 start;
6381200a7d9SSimran Rai 	struct ringbuf_regs *p_rbuf = NULL;
6391200a7d9SSimran Rai 
6401200a7d9SSimran Rai 	aio = cygnus_dai_get_dma_data(substream);
641fc392364SKuninori Morimoto 	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
6421200a7d9SSimran Rai 
6431200a7d9SSimran Rai 	bufsize = snd_pcm_lib_buffer_bytes(substream);
6441200a7d9SSimran Rai 	periodsize = snd_pcm_lib_period_bytes(substream);
6451200a7d9SSimran Rai 
646fc392364SKuninori Morimoto 	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s (buf_size %lu) (period_size %lu)\n",
6471200a7d9SSimran Rai 			__func__, bufsize, periodsize);
6481200a7d9SSimran Rai 
6491200a7d9SSimran Rai 	configure_ringbuf_regs(substream);
6501200a7d9SSimran Rai 
6511200a7d9SSimran Rai 	p_rbuf = get_ringbuf(substream);
6521200a7d9SSimran Rai 
6531200a7d9SSimran Rai 	start = runtime->dma_addr;
6541200a7d9SSimran Rai 
6551200a7d9SSimran Rai 	is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
6561200a7d9SSimran Rai 
6571200a7d9SSimran Rai 	ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start,
6581200a7d9SSimran Rai 				periodsize, bufsize);
6591200a7d9SSimran Rai 
660e1fb714bSHariprasad Kelam 	return 0;
6611200a7d9SSimran Rai }
6621200a7d9SSimran Rai 
cygnus_pcm_pointer(struct snd_soc_component * component,struct snd_pcm_substream * substream)663dc06bd0fSKuninori Morimoto static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component,
664dc06bd0fSKuninori Morimoto 					    struct snd_pcm_substream *substream)
6651200a7d9SSimran Rai {
6661200a7d9SSimran Rai 	struct cygnus_aio_port *aio;
6671200a7d9SSimran Rai 	unsigned int res = 0, cur = 0, base = 0;
6681200a7d9SSimran Rai 	struct ringbuf_regs *p_rbuf = NULL;
6691200a7d9SSimran Rai 
6701200a7d9SSimran Rai 	aio = cygnus_dai_get_dma_data(substream);
6711200a7d9SSimran Rai 
6721200a7d9SSimran Rai 	/*
6731200a7d9SSimran Rai 	 * Get the offset of the current read (for playack) or write
6741200a7d9SSimran Rai 	 * index (for capture).  Report this value back to the asoc framework.
6751200a7d9SSimran Rai 	 */
6761200a7d9SSimran Rai 	p_rbuf = get_ringbuf(substream);
6771200a7d9SSimran Rai 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
6781200a7d9SSimran Rai 		cur = readl(aio->cygaud->audio + p_rbuf->rdaddr);
6791200a7d9SSimran Rai 	else
6801200a7d9SSimran Rai 		cur = readl(aio->cygaud->audio + p_rbuf->wraddr);
6811200a7d9SSimran Rai 
6821200a7d9SSimran Rai 	base = readl(aio->cygaud->audio + p_rbuf->baseaddr);
6831200a7d9SSimran Rai 
6841200a7d9SSimran Rai 	/*
6851200a7d9SSimran Rai 	 * Mask off the MSB of the rdaddr,wraddr and baseaddr
6861200a7d9SSimran Rai 	 * since MSB is not part of the address
6871200a7d9SSimran Rai 	 */
6881200a7d9SSimran Rai 	res = (cur & 0x7fffffff) - (base & 0x7fffffff);
6891200a7d9SSimran Rai 
6901200a7d9SSimran Rai 	return bytes_to_frames(substream->runtime, res);
6911200a7d9SSimran Rai }
6921200a7d9SSimran Rai 
cygnus_dma_new(struct snd_soc_component * component,struct snd_soc_pcm_runtime * rtd)693dc06bd0fSKuninori Morimoto static int cygnus_dma_new(struct snd_soc_component *component,
694dc06bd0fSKuninori Morimoto 			  struct snd_soc_pcm_runtime *rtd)
6951200a7d9SSimran Rai {
6965ac813c8SLars-Peter Clausen 	size_t size = cygnus_pcm_hw.buffer_bytes_max;
6971200a7d9SSimran Rai 	struct snd_card *card = rtd->card->snd_card;
6981200a7d9SSimran Rai 
6991200a7d9SSimran Rai 	if (!card->dev->dma_mask)
7001200a7d9SSimran Rai 		card->dev->dma_mask = &cygnus_dma_dmamask;
7011200a7d9SSimran Rai 	if (!card->dev->coherent_dma_mask)
7021200a7d9SSimran Rai 		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
7031200a7d9SSimran Rai 
7045ac813c8SLars-Peter Clausen 	snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
7055ac813c8SLars-Peter Clausen 				       card->dev, size, size);
7061200a7d9SSimran Rai 
7071200a7d9SSimran Rai 	return 0;
7081200a7d9SSimran Rai }
7091200a7d9SSimran Rai 
710540b925aSKuninori Morimoto static struct snd_soc_component_driver cygnus_soc_platform = {
711dc06bd0fSKuninori Morimoto 	.open		= cygnus_pcm_open,
712dc06bd0fSKuninori Morimoto 	.close		= cygnus_pcm_close,
713dc06bd0fSKuninori Morimoto 	.prepare	= cygnus_pcm_prepare,
714dc06bd0fSKuninori Morimoto 	.trigger	= cygnus_pcm_trigger,
715dc06bd0fSKuninori Morimoto 	.pointer	= cygnus_pcm_pointer,
716dc06bd0fSKuninori Morimoto 	.pcm_construct	= cygnus_dma_new,
7171200a7d9SSimran Rai };
7181200a7d9SSimran Rai 
cygnus_soc_platform_register(struct device * dev,struct cygnus_audio * cygaud)7191200a7d9SSimran Rai int cygnus_soc_platform_register(struct device *dev,
7201200a7d9SSimran Rai 				 struct cygnus_audio *cygaud)
7211200a7d9SSimran Rai {
72214db5499STang Bin 	int rc;
7231200a7d9SSimran Rai 
7241200a7d9SSimran Rai 	dev_dbg(dev, "%s Enter\n", __func__);
7251200a7d9SSimran Rai 
7261200a7d9SSimran Rai 	rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq,
7271200a7d9SSimran Rai 				IRQF_SHARED, "cygnus-audio", cygaud);
7281200a7d9SSimran Rai 	if (rc) {
7291200a7d9SSimran Rai 		dev_err(dev, "%s request_irq error %d\n", __func__, rc);
7301200a7d9SSimran Rai 		return rc;
7311200a7d9SSimran Rai 	}
7321200a7d9SSimran Rai 
733540b925aSKuninori Morimoto 	rc = devm_snd_soc_register_component(dev, &cygnus_soc_platform,
734540b925aSKuninori Morimoto 					     NULL, 0);
7351200a7d9SSimran Rai 	if (rc) {
7361200a7d9SSimran Rai 		dev_err(dev, "%s failed\n", __func__);
7371200a7d9SSimran Rai 		return rc;
7381200a7d9SSimran Rai 	}
7391200a7d9SSimran Rai 
7401200a7d9SSimran Rai 	return 0;
7411200a7d9SSimran Rai }
7421200a7d9SSimran Rai 
cygnus_soc_platform_unregister(struct device * dev)7431200a7d9SSimran Rai int cygnus_soc_platform_unregister(struct device *dev)
7441200a7d9SSimran Rai {
7451200a7d9SSimran Rai 	return 0;
7461200a7d9SSimran Rai }
7471200a7d9SSimran Rai 
7481200a7d9SSimran Rai MODULE_LICENSE("GPL v2");
7491200a7d9SSimran Rai MODULE_AUTHOR("Broadcom");
7501200a7d9SSimran Rai MODULE_DESCRIPTION("Cygnus ASoC PCM module");
751