xref: /OK3568_Linux_fs/kernel/sound/soc/bcm/cygnus-pcm.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright (C) 2014-2015 Broadcom Corporation
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * This program is free software; you can redistribute it and/or
5*4882a593Smuzhiyun  * modify it under the terms of the GNU General Public License as
6*4882a593Smuzhiyun  * published by the Free Software Foundation version 2.
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
9*4882a593Smuzhiyun  * kind, whether express or implied; without even the implied warranty
10*4882a593Smuzhiyun  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11*4882a593Smuzhiyun  * GNU General Public License for more details.
12*4882a593Smuzhiyun  */
13*4882a593Smuzhiyun #include <linux/debugfs.h>
14*4882a593Smuzhiyun #include <linux/dma-mapping.h>
15*4882a593Smuzhiyun #include <linux/init.h>
16*4882a593Smuzhiyun #include <linux/io.h>
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <linux/slab.h>
19*4882a593Smuzhiyun #include <linux/timer.h>
20*4882a593Smuzhiyun #include <sound/core.h>
21*4882a593Smuzhiyun #include <sound/pcm.h>
22*4882a593Smuzhiyun #include <sound/pcm_params.h>
23*4882a593Smuzhiyun #include <sound/soc.h>
24*4882a593Smuzhiyun #include <sound/soc-dai.h>
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun #include "cygnus-ssp.h"
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun /* Register offset needed for ASoC PCM module */
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #define INTH_R5F_STATUS_OFFSET     0x040
31*4882a593Smuzhiyun #define INTH_R5F_CLEAR_OFFSET      0x048
32*4882a593Smuzhiyun #define INTH_R5F_MASK_SET_OFFSET   0x050
33*4882a593Smuzhiyun #define INTH_R5F_MASK_CLEAR_OFFSET 0x054
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun #define BF_REARM_FREE_MARK_OFFSET 0x344
36*4882a593Smuzhiyun #define BF_REARM_FULL_MARK_OFFSET 0x348
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun /* Ring Buffer Ctrl Regs --- Start */
39*4882a593Smuzhiyun /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_RDADDR_REG_BASE */
40*4882a593Smuzhiyun #define SRC_RBUF_0_RDADDR_OFFSET 0x500
41*4882a593Smuzhiyun #define SRC_RBUF_1_RDADDR_OFFSET 0x518
42*4882a593Smuzhiyun #define SRC_RBUF_2_RDADDR_OFFSET 0x530
43*4882a593Smuzhiyun #define SRC_RBUF_3_RDADDR_OFFSET 0x548
44*4882a593Smuzhiyun #define SRC_RBUF_4_RDADDR_OFFSET 0x560
45*4882a593Smuzhiyun #define SRC_RBUF_5_RDADDR_OFFSET 0x578
46*4882a593Smuzhiyun #define SRC_RBUF_6_RDADDR_OFFSET 0x590
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_WRADDR_REG_BASE */
49*4882a593Smuzhiyun #define SRC_RBUF_0_WRADDR_OFFSET 0x504
50*4882a593Smuzhiyun #define SRC_RBUF_1_WRADDR_OFFSET 0x51c
51*4882a593Smuzhiyun #define SRC_RBUF_2_WRADDR_OFFSET 0x534
52*4882a593Smuzhiyun #define SRC_RBUF_3_WRADDR_OFFSET 0x54c
53*4882a593Smuzhiyun #define SRC_RBUF_4_WRADDR_OFFSET 0x564
54*4882a593Smuzhiyun #define SRC_RBUF_5_WRADDR_OFFSET 0x57c
55*4882a593Smuzhiyun #define SRC_RBUF_6_WRADDR_OFFSET 0x594
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_BASEADDR_REG_BASE */
58*4882a593Smuzhiyun #define SRC_RBUF_0_BASEADDR_OFFSET 0x508
59*4882a593Smuzhiyun #define SRC_RBUF_1_BASEADDR_OFFSET 0x520
60*4882a593Smuzhiyun #define SRC_RBUF_2_BASEADDR_OFFSET 0x538
61*4882a593Smuzhiyun #define SRC_RBUF_3_BASEADDR_OFFSET 0x550
62*4882a593Smuzhiyun #define SRC_RBUF_4_BASEADDR_OFFSET 0x568
63*4882a593Smuzhiyun #define SRC_RBUF_5_BASEADDR_OFFSET 0x580
64*4882a593Smuzhiyun #define SRC_RBUF_6_BASEADDR_OFFSET 0x598
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_ENDADDR_REG_BASE */
67*4882a593Smuzhiyun #define SRC_RBUF_0_ENDADDR_OFFSET 0x50c
68*4882a593Smuzhiyun #define SRC_RBUF_1_ENDADDR_OFFSET 0x524
69*4882a593Smuzhiyun #define SRC_RBUF_2_ENDADDR_OFFSET 0x53c
70*4882a593Smuzhiyun #define SRC_RBUF_3_ENDADDR_OFFSET 0x554
71*4882a593Smuzhiyun #define SRC_RBUF_4_ENDADDR_OFFSET 0x56c
72*4882a593Smuzhiyun #define SRC_RBUF_5_ENDADDR_OFFSET 0x584
73*4882a593Smuzhiyun #define SRC_RBUF_6_ENDADDR_OFFSET 0x59c
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun /* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_FREE_MARK_REG_BASE */
76*4882a593Smuzhiyun #define SRC_RBUF_0_FREE_MARK_OFFSET 0x510
77*4882a593Smuzhiyun #define SRC_RBUF_1_FREE_MARK_OFFSET 0x528
78*4882a593Smuzhiyun #define SRC_RBUF_2_FREE_MARK_OFFSET 0x540
79*4882a593Smuzhiyun #define SRC_RBUF_3_FREE_MARK_OFFSET 0x558
80*4882a593Smuzhiyun #define SRC_RBUF_4_FREE_MARK_OFFSET 0x570
81*4882a593Smuzhiyun #define SRC_RBUF_5_FREE_MARK_OFFSET 0x588
82*4882a593Smuzhiyun #define SRC_RBUF_6_FREE_MARK_OFFSET 0x5a0
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_RDADDR_REG_BASE */
85*4882a593Smuzhiyun #define DST_RBUF_0_RDADDR_OFFSET 0x5c0
86*4882a593Smuzhiyun #define DST_RBUF_1_RDADDR_OFFSET 0x5d8
87*4882a593Smuzhiyun #define DST_RBUF_2_RDADDR_OFFSET 0x5f0
88*4882a593Smuzhiyun #define DST_RBUF_3_RDADDR_OFFSET 0x608
89*4882a593Smuzhiyun #define DST_RBUF_4_RDADDR_OFFSET 0x620
90*4882a593Smuzhiyun #define DST_RBUF_5_RDADDR_OFFSET 0x638
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_WRADDR_REG_BASE */
93*4882a593Smuzhiyun #define DST_RBUF_0_WRADDR_OFFSET 0x5c4
94*4882a593Smuzhiyun #define DST_RBUF_1_WRADDR_OFFSET 0x5dc
95*4882a593Smuzhiyun #define DST_RBUF_2_WRADDR_OFFSET 0x5f4
96*4882a593Smuzhiyun #define DST_RBUF_3_WRADDR_OFFSET 0x60c
97*4882a593Smuzhiyun #define DST_RBUF_4_WRADDR_OFFSET 0x624
98*4882a593Smuzhiyun #define DST_RBUF_5_WRADDR_OFFSET 0x63c
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_BASEADDR_REG_BASE */
101*4882a593Smuzhiyun #define DST_RBUF_0_BASEADDR_OFFSET 0x5c8
102*4882a593Smuzhiyun #define DST_RBUF_1_BASEADDR_OFFSET 0x5e0
103*4882a593Smuzhiyun #define DST_RBUF_2_BASEADDR_OFFSET 0x5f8
104*4882a593Smuzhiyun #define DST_RBUF_3_BASEADDR_OFFSET 0x610
105*4882a593Smuzhiyun #define DST_RBUF_4_BASEADDR_OFFSET 0x628
106*4882a593Smuzhiyun #define DST_RBUF_5_BASEADDR_OFFSET 0x640
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_ENDADDR_REG_BASE */
109*4882a593Smuzhiyun #define DST_RBUF_0_ENDADDR_OFFSET 0x5cc
110*4882a593Smuzhiyun #define DST_RBUF_1_ENDADDR_OFFSET 0x5e4
111*4882a593Smuzhiyun #define DST_RBUF_2_ENDADDR_OFFSET 0x5fc
112*4882a593Smuzhiyun #define DST_RBUF_3_ENDADDR_OFFSET 0x614
113*4882a593Smuzhiyun #define DST_RBUF_4_ENDADDR_OFFSET 0x62c
114*4882a593Smuzhiyun #define DST_RBUF_5_ENDADDR_OFFSET 0x644
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun /* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_FULL_MARK_REG_BASE */
117*4882a593Smuzhiyun #define DST_RBUF_0_FULL_MARK_OFFSET 0x5d0
118*4882a593Smuzhiyun #define DST_RBUF_1_FULL_MARK_OFFSET 0x5e8
119*4882a593Smuzhiyun #define DST_RBUF_2_FULL_MARK_OFFSET 0x600
120*4882a593Smuzhiyun #define DST_RBUF_3_FULL_MARK_OFFSET 0x618
121*4882a593Smuzhiyun #define DST_RBUF_4_FULL_MARK_OFFSET 0x630
122*4882a593Smuzhiyun #define DST_RBUF_5_FULL_MARK_OFFSET 0x648
123*4882a593Smuzhiyun /* Ring Buffer Ctrl Regs --- End */
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun /* Error Status Regs --- Start */
126*4882a593Smuzhiyun /* AUD_FMM_BF_ESR_ESRX_STATUS_REG_BASE */
127*4882a593Smuzhiyun #define ESR0_STATUS_OFFSET 0x900
128*4882a593Smuzhiyun #define ESR1_STATUS_OFFSET 0x918
129*4882a593Smuzhiyun #define ESR2_STATUS_OFFSET 0x930
130*4882a593Smuzhiyun #define ESR3_STATUS_OFFSET 0x948
131*4882a593Smuzhiyun #define ESR4_STATUS_OFFSET 0x960
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun /* AUD_FMM_BF_ESR_ESRX_STATUS_CLEAR_REG_BASE */
134*4882a593Smuzhiyun #define ESR0_STATUS_CLR_OFFSET 0x908
135*4882a593Smuzhiyun #define ESR1_STATUS_CLR_OFFSET 0x920
136*4882a593Smuzhiyun #define ESR2_STATUS_CLR_OFFSET 0x938
137*4882a593Smuzhiyun #define ESR3_STATUS_CLR_OFFSET 0x950
138*4882a593Smuzhiyun #define ESR4_STATUS_CLR_OFFSET 0x968
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun /* AUD_FMM_BF_ESR_ESRX_MASK_REG_BASE */
141*4882a593Smuzhiyun #define ESR0_MASK_STATUS_OFFSET 0x90c
142*4882a593Smuzhiyun #define ESR1_MASK_STATUS_OFFSET 0x924
143*4882a593Smuzhiyun #define ESR2_MASK_STATUS_OFFSET 0x93c
144*4882a593Smuzhiyun #define ESR3_MASK_STATUS_OFFSET 0x954
145*4882a593Smuzhiyun #define ESR4_MASK_STATUS_OFFSET 0x96c
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun /* AUD_FMM_BF_ESR_ESRX_MASK_SET_REG_BASE */
148*4882a593Smuzhiyun #define ESR0_MASK_SET_OFFSET 0x910
149*4882a593Smuzhiyun #define ESR1_MASK_SET_OFFSET 0x928
150*4882a593Smuzhiyun #define ESR2_MASK_SET_OFFSET 0x940
151*4882a593Smuzhiyun #define ESR3_MASK_SET_OFFSET 0x958
152*4882a593Smuzhiyun #define ESR4_MASK_SET_OFFSET 0x970
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun /* AUD_FMM_BF_ESR_ESRX_MASK_CLEAR_REG_BASE */
155*4882a593Smuzhiyun #define ESR0_MASK_CLR_OFFSET 0x914
156*4882a593Smuzhiyun #define ESR1_MASK_CLR_OFFSET 0x92c
157*4882a593Smuzhiyun #define ESR2_MASK_CLR_OFFSET 0x944
158*4882a593Smuzhiyun #define ESR3_MASK_CLR_OFFSET 0x95c
159*4882a593Smuzhiyun #define ESR4_MASK_CLR_OFFSET 0x974
160*4882a593Smuzhiyun /* Error Status Regs --- End */
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun #define R5F_ESR0_SHIFT  0    /* esr0 = fifo underflow */
163*4882a593Smuzhiyun #define R5F_ESR1_SHIFT  1    /* esr1 = ringbuf underflow */
164*4882a593Smuzhiyun #define R5F_ESR2_SHIFT  2    /* esr2 = ringbuf overflow */
165*4882a593Smuzhiyun #define R5F_ESR3_SHIFT  3    /* esr3 = freemark */
166*4882a593Smuzhiyun #define R5F_ESR4_SHIFT  4    /* esr4 = fullmark */
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun /* Mask for R5F register.  Set all relevant interrupt for playback handler */
170*4882a593Smuzhiyun #define ANY_PLAYBACK_IRQ  (BIT(R5F_ESR0_SHIFT) | \
171*4882a593Smuzhiyun 			   BIT(R5F_ESR1_SHIFT) | \
172*4882a593Smuzhiyun 			   BIT(R5F_ESR3_SHIFT))
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun /* Mask for R5F register.  Set all relevant interrupt for capture handler */
175*4882a593Smuzhiyun #define ANY_CAPTURE_IRQ   (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT))
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun /*
178*4882a593Smuzhiyun  * PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick.
179*4882a593Smuzhiyun  * This number should be a multiple of 256. Minimum value is 256
180*4882a593Smuzhiyun  */
181*4882a593Smuzhiyun #define PERIOD_BYTES_MIN 0x100
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun static const struct snd_pcm_hardware cygnus_pcm_hw = {
184*4882a593Smuzhiyun 	.info = SNDRV_PCM_INFO_MMAP |
185*4882a593Smuzhiyun 			SNDRV_PCM_INFO_MMAP_VALID |
186*4882a593Smuzhiyun 			SNDRV_PCM_INFO_INTERLEAVED,
187*4882a593Smuzhiyun 	.formats = SNDRV_PCM_FMTBIT_S16_LE |
188*4882a593Smuzhiyun 			SNDRV_PCM_FMTBIT_S32_LE,
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 	/* A period is basically an interrupt */
191*4882a593Smuzhiyun 	.period_bytes_min = PERIOD_BYTES_MIN,
192*4882a593Smuzhiyun 	.period_bytes_max = 0x10000,
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	/* period_min/max gives range of approx interrupts per buffer */
195*4882a593Smuzhiyun 	.periods_min = 2,
196*4882a593Smuzhiyun 	.periods_max = 8,
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	/*
199*4882a593Smuzhiyun 	 * maximum buffer size in bytes = period_bytes_max * periods_max
200*4882a593Smuzhiyun 	 * We allocate this amount of data for each enabled channel
201*4882a593Smuzhiyun 	 */
202*4882a593Smuzhiyun 	.buffer_bytes_max = 4 * 0x8000,
203*4882a593Smuzhiyun };
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun static u64 cygnus_dma_dmamask = DMA_BIT_MASK(32);
206*4882a593Smuzhiyun 
cygnus_dai_get_dma_data(struct snd_pcm_substream * substream)207*4882a593Smuzhiyun static struct cygnus_aio_port *cygnus_dai_get_dma_data(
208*4882a593Smuzhiyun 				struct snd_pcm_substream *substream)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	return snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(soc_runtime, 0), substream);
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun 
ringbuf_set_initial(void __iomem * audio_io,struct ringbuf_regs * p_rbuf,bool is_playback,u32 start,u32 periodsize,u32 bufsize)215*4882a593Smuzhiyun static void ringbuf_set_initial(void __iomem *audio_io,
216*4882a593Smuzhiyun 		struct ringbuf_regs *p_rbuf,
217*4882a593Smuzhiyun 		bool is_playback,
218*4882a593Smuzhiyun 		u32 start,
219*4882a593Smuzhiyun 		u32 periodsize,
220*4882a593Smuzhiyun 		u32 bufsize)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun 	u32 initial_rd;
223*4882a593Smuzhiyun 	u32 initial_wr;
224*4882a593Smuzhiyun 	u32 end;
225*4882a593Smuzhiyun 	u32 fmark_val; /* free or full mark */
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	p_rbuf->period_bytes = periodsize;
228*4882a593Smuzhiyun 	p_rbuf->buf_size = bufsize;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	if (is_playback) {
231*4882a593Smuzhiyun 		/* Set the pointers to indicate full (flip uppermost bit) */
232*4882a593Smuzhiyun 		initial_rd = start;
233*4882a593Smuzhiyun 		initial_wr = initial_rd ^ BIT(31);
234*4882a593Smuzhiyun 	} else {
235*4882a593Smuzhiyun 		/* Set the pointers to indicate empty */
236*4882a593Smuzhiyun 		initial_wr = start;
237*4882a593Smuzhiyun 		initial_rd = initial_wr;
238*4882a593Smuzhiyun 	}
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	end = start + bufsize - 1;
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	/*
243*4882a593Smuzhiyun 	 * The interrupt will fire when free/full mark is *exceeded*
244*4882a593Smuzhiyun 	 * The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark
245*4882a593Smuzhiyun 	 * to be PERIOD_BYTES_MIN less than the period size.
246*4882a593Smuzhiyun 	 */
247*4882a593Smuzhiyun 	fmark_val = periodsize - PERIOD_BYTES_MIN;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	writel(start, audio_io + p_rbuf->baseaddr);
250*4882a593Smuzhiyun 	writel(end, audio_io + p_rbuf->endaddr);
251*4882a593Smuzhiyun 	writel(fmark_val, audio_io + p_rbuf->fmark);
252*4882a593Smuzhiyun 	writel(initial_rd, audio_io + p_rbuf->rdaddr);
253*4882a593Smuzhiyun 	writel(initial_wr, audio_io + p_rbuf->wraddr);
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun 
configure_ringbuf_regs(struct snd_pcm_substream * substream)256*4882a593Smuzhiyun static int configure_ringbuf_regs(struct snd_pcm_substream *substream)
257*4882a593Smuzhiyun {
258*4882a593Smuzhiyun 	struct cygnus_aio_port *aio;
259*4882a593Smuzhiyun 	struct ringbuf_regs *p_rbuf;
260*4882a593Smuzhiyun 	int status = 0;
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	aio = cygnus_dai_get_dma_data(substream);
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	/* Map the ssp portnum to a set of ring buffers. */
265*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
266*4882a593Smuzhiyun 		p_rbuf = &aio->play_rb_regs;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 		switch (aio->portnum) {
269*4882a593Smuzhiyun 		case 0:
270*4882a593Smuzhiyun 			*p_rbuf = RINGBUF_REG_PLAYBACK(0);
271*4882a593Smuzhiyun 			break;
272*4882a593Smuzhiyun 		case 1:
273*4882a593Smuzhiyun 			*p_rbuf = RINGBUF_REG_PLAYBACK(2);
274*4882a593Smuzhiyun 			break;
275*4882a593Smuzhiyun 		case 2:
276*4882a593Smuzhiyun 			*p_rbuf = RINGBUF_REG_PLAYBACK(4);
277*4882a593Smuzhiyun 			break;
278*4882a593Smuzhiyun 		case 3: /* SPDIF */
279*4882a593Smuzhiyun 			*p_rbuf = RINGBUF_REG_PLAYBACK(6);
280*4882a593Smuzhiyun 			break;
281*4882a593Smuzhiyun 		default:
282*4882a593Smuzhiyun 			status = -EINVAL;
283*4882a593Smuzhiyun 		}
284*4882a593Smuzhiyun 	} else {
285*4882a593Smuzhiyun 		p_rbuf = &aio->capture_rb_regs;
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 		switch (aio->portnum) {
288*4882a593Smuzhiyun 		case 0:
289*4882a593Smuzhiyun 			*p_rbuf = RINGBUF_REG_CAPTURE(0);
290*4882a593Smuzhiyun 			break;
291*4882a593Smuzhiyun 		case 1:
292*4882a593Smuzhiyun 			*p_rbuf = RINGBUF_REG_CAPTURE(2);
293*4882a593Smuzhiyun 			break;
294*4882a593Smuzhiyun 		case 2:
295*4882a593Smuzhiyun 			*p_rbuf = RINGBUF_REG_CAPTURE(4);
296*4882a593Smuzhiyun 			break;
297*4882a593Smuzhiyun 		default:
298*4882a593Smuzhiyun 			status = -EINVAL;
299*4882a593Smuzhiyun 		}
300*4882a593Smuzhiyun 	}
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	return status;
303*4882a593Smuzhiyun }
304*4882a593Smuzhiyun 
get_ringbuf(struct snd_pcm_substream * substream)305*4882a593Smuzhiyun static struct ringbuf_regs *get_ringbuf(struct snd_pcm_substream *substream)
306*4882a593Smuzhiyun {
307*4882a593Smuzhiyun 	struct cygnus_aio_port *aio;
308*4882a593Smuzhiyun 	struct ringbuf_regs *p_rbuf = NULL;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	aio = cygnus_dai_get_dma_data(substream);
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
313*4882a593Smuzhiyun 		p_rbuf = &aio->play_rb_regs;
314*4882a593Smuzhiyun 	else
315*4882a593Smuzhiyun 		p_rbuf = &aio->capture_rb_regs;
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	return p_rbuf;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun 
enable_intr(struct snd_pcm_substream * substream)320*4882a593Smuzhiyun static void enable_intr(struct snd_pcm_substream *substream)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun 	struct cygnus_aio_port *aio;
323*4882a593Smuzhiyun 	u32 clear_mask;
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun 	aio = cygnus_dai_get_dma_data(substream);
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	/* The port number maps to the bit position to be cleared */
328*4882a593Smuzhiyun 	clear_mask = BIT(aio->portnum);
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
331*4882a593Smuzhiyun 		/* Clear interrupt status before enabling them */
332*4882a593Smuzhiyun 		writel(clear_mask, aio->cygaud->audio + ESR0_STATUS_CLR_OFFSET);
333*4882a593Smuzhiyun 		writel(clear_mask, aio->cygaud->audio + ESR1_STATUS_CLR_OFFSET);
334*4882a593Smuzhiyun 		writel(clear_mask, aio->cygaud->audio + ESR3_STATUS_CLR_OFFSET);
335*4882a593Smuzhiyun 		/* Unmask the interrupts of the given port*/
336*4882a593Smuzhiyun 		writel(clear_mask, aio->cygaud->audio + ESR0_MASK_CLR_OFFSET);
337*4882a593Smuzhiyun 		writel(clear_mask, aio->cygaud->audio + ESR1_MASK_CLR_OFFSET);
338*4882a593Smuzhiyun 		writel(clear_mask, aio->cygaud->audio + ESR3_MASK_CLR_OFFSET);
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 		writel(ANY_PLAYBACK_IRQ,
341*4882a593Smuzhiyun 			aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
342*4882a593Smuzhiyun 	} else {
343*4882a593Smuzhiyun 		writel(clear_mask, aio->cygaud->audio + ESR2_STATUS_CLR_OFFSET);
344*4882a593Smuzhiyun 		writel(clear_mask, aio->cygaud->audio + ESR4_STATUS_CLR_OFFSET);
345*4882a593Smuzhiyun 		writel(clear_mask, aio->cygaud->audio + ESR2_MASK_CLR_OFFSET);
346*4882a593Smuzhiyun 		writel(clear_mask, aio->cygaud->audio + ESR4_MASK_CLR_OFFSET);
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 		writel(ANY_CAPTURE_IRQ,
349*4882a593Smuzhiyun 			aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
350*4882a593Smuzhiyun 	}
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun 
disable_intr(struct snd_pcm_substream * substream)354*4882a593Smuzhiyun static void disable_intr(struct snd_pcm_substream *substream)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
357*4882a593Smuzhiyun 	struct cygnus_aio_port *aio;
358*4882a593Smuzhiyun 	u32 set_mask;
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	aio = cygnus_dai_get_dma_data(substream);
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s on port %d\n", __func__, aio->portnum);
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	/* The port number maps to the bit position to be set */
365*4882a593Smuzhiyun 	set_mask = BIT(aio->portnum);
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
368*4882a593Smuzhiyun 		/* Mask the interrupts of the given port*/
369*4882a593Smuzhiyun 		writel(set_mask, aio->cygaud->audio + ESR0_MASK_SET_OFFSET);
370*4882a593Smuzhiyun 		writel(set_mask, aio->cygaud->audio + ESR1_MASK_SET_OFFSET);
371*4882a593Smuzhiyun 		writel(set_mask, aio->cygaud->audio + ESR3_MASK_SET_OFFSET);
372*4882a593Smuzhiyun 	} else {
373*4882a593Smuzhiyun 		writel(set_mask, aio->cygaud->audio + ESR2_MASK_SET_OFFSET);
374*4882a593Smuzhiyun 		writel(set_mask, aio->cygaud->audio + ESR4_MASK_SET_OFFSET);
375*4882a593Smuzhiyun 	}
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun 
cygnus_pcm_trigger(struct snd_soc_component * component,struct snd_pcm_substream * substream,int cmd)379*4882a593Smuzhiyun static int cygnus_pcm_trigger(struct snd_soc_component *component,
380*4882a593Smuzhiyun 			      struct snd_pcm_substream *substream, int cmd)
381*4882a593Smuzhiyun {
382*4882a593Smuzhiyun 	int ret = 0;
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	switch (cmd) {
385*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_START:
386*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_RESUME:
387*4882a593Smuzhiyun 		enable_intr(substream);
388*4882a593Smuzhiyun 		break;
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_STOP:
391*4882a593Smuzhiyun 	case SNDRV_PCM_TRIGGER_SUSPEND:
392*4882a593Smuzhiyun 		disable_intr(substream);
393*4882a593Smuzhiyun 		break;
394*4882a593Smuzhiyun 	default:
395*4882a593Smuzhiyun 		ret = -EINVAL;
396*4882a593Smuzhiyun 	}
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	return ret;
399*4882a593Smuzhiyun }
400*4882a593Smuzhiyun 
cygnus_pcm_period_elapsed(struct snd_pcm_substream * substream)401*4882a593Smuzhiyun static void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun 	struct cygnus_aio_port *aio;
404*4882a593Smuzhiyun 	struct ringbuf_regs *p_rbuf = NULL;
405*4882a593Smuzhiyun 	u32 regval;
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	aio = cygnus_dai_get_dma_data(substream);
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	p_rbuf = get_ringbuf(substream);
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	/*
412*4882a593Smuzhiyun 	 * If free/full mark interrupt occurs, provide timestamp
413*4882a593Smuzhiyun 	 * to ALSA and update appropriate idx by period_bytes
414*4882a593Smuzhiyun 	 */
415*4882a593Smuzhiyun 	snd_pcm_period_elapsed(substream);
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
418*4882a593Smuzhiyun 		/* Set the ring buffer to full */
419*4882a593Smuzhiyun 		regval = readl(aio->cygaud->audio + p_rbuf->rdaddr);
420*4882a593Smuzhiyun 		regval = regval ^ BIT(31);
421*4882a593Smuzhiyun 		writel(regval, aio->cygaud->audio + p_rbuf->wraddr);
422*4882a593Smuzhiyun 	} else {
423*4882a593Smuzhiyun 		/* Set the ring buffer to empty */
424*4882a593Smuzhiyun 		regval = readl(aio->cygaud->audio + p_rbuf->wraddr);
425*4882a593Smuzhiyun 		writel(regval, aio->cygaud->audio + p_rbuf->rdaddr);
426*4882a593Smuzhiyun 	}
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun /*
430*4882a593Smuzhiyun  * ESR0/1/3 status  Description
431*4882a593Smuzhiyun  *  0x1	I2S0_out port caused interrupt
432*4882a593Smuzhiyun  *  0x2	I2S1_out port caused interrupt
433*4882a593Smuzhiyun  *  0x4	I2S2_out port caused interrupt
434*4882a593Smuzhiyun  *  0x8	SPDIF_out port caused interrupt
435*4882a593Smuzhiyun  */
handle_playback_irq(struct cygnus_audio * cygaud)436*4882a593Smuzhiyun static void handle_playback_irq(struct cygnus_audio *cygaud)
437*4882a593Smuzhiyun {
438*4882a593Smuzhiyun 	void __iomem *audio_io;
439*4882a593Smuzhiyun 	u32 port;
440*4882a593Smuzhiyun 	u32 esr_status0, esr_status1, esr_status3;
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	audio_io = cygaud->audio;
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 	/*
445*4882a593Smuzhiyun 	 * ESR status gets updates with/without interrupts enabled.
446*4882a593Smuzhiyun 	 * So, check the ESR mask, which provides interrupt enable/
447*4882a593Smuzhiyun 	 * disable status and use it to determine which ESR status
448*4882a593Smuzhiyun 	 * should be serviced.
449*4882a593Smuzhiyun 	 */
450*4882a593Smuzhiyun 	esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET);
451*4882a593Smuzhiyun 	esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET);
452*4882a593Smuzhiyun 	esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET);
453*4882a593Smuzhiyun 	esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET);
454*4882a593Smuzhiyun 	esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET);
455*4882a593Smuzhiyun 	esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET);
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) {
458*4882a593Smuzhiyun 		u32 esrmask = BIT(port);
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 		/*
461*4882a593Smuzhiyun 		 * Ringbuffer or FIFO underflow
462*4882a593Smuzhiyun 		 * If we get this interrupt then, it is also true that we have
463*4882a593Smuzhiyun 		 * not yet responded to the freemark interrupt.
464*4882a593Smuzhiyun 		 * Log a debug message.  The freemark handler below will
465*4882a593Smuzhiyun 		 * handle getting everything going again.
466*4882a593Smuzhiyun 		 */
467*4882a593Smuzhiyun 		if ((esrmask & esr_status1) || (esrmask & esr_status0)) {
468*4882a593Smuzhiyun 			dev_dbg(cygaud->dev,
469*4882a593Smuzhiyun 				"Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n",
470*4882a593Smuzhiyun 				esr_status0, esr_status1, esr_status3);
471*4882a593Smuzhiyun 		}
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 		/*
474*4882a593Smuzhiyun 		 * Freemark is hit. This is the normal interrupt.
475*4882a593Smuzhiyun 		 * In typical operation the read and write regs will be equal
476*4882a593Smuzhiyun 		 */
477*4882a593Smuzhiyun 		if (esrmask & esr_status3) {
478*4882a593Smuzhiyun 			struct snd_pcm_substream *playstr;
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 			playstr = cygaud->portinfo[port].play_stream;
481*4882a593Smuzhiyun 			cygnus_pcm_period_elapsed(playstr);
482*4882a593Smuzhiyun 		}
483*4882a593Smuzhiyun 	}
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	/* Clear ESR interrupt */
486*4882a593Smuzhiyun 	writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET);
487*4882a593Smuzhiyun 	writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET);
488*4882a593Smuzhiyun 	writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET);
489*4882a593Smuzhiyun 	/* Rearm freemark logic by writing 1 to the correct bit */
490*4882a593Smuzhiyun 	writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET);
491*4882a593Smuzhiyun }
492*4882a593Smuzhiyun 
493*4882a593Smuzhiyun /*
494*4882a593Smuzhiyun  * ESR2/4 status  Description
495*4882a593Smuzhiyun  *  0x1	I2S0_in port caused interrupt
496*4882a593Smuzhiyun  *  0x2	I2S1_in port caused interrupt
497*4882a593Smuzhiyun  *  0x4	I2S2_in port caused interrupt
498*4882a593Smuzhiyun  */
handle_capture_irq(struct cygnus_audio * cygaud)499*4882a593Smuzhiyun static void handle_capture_irq(struct cygnus_audio *cygaud)
500*4882a593Smuzhiyun {
501*4882a593Smuzhiyun 	void __iomem *audio_io;
502*4882a593Smuzhiyun 	u32 port;
503*4882a593Smuzhiyun 	u32 esr_status2, esr_status4;
504*4882a593Smuzhiyun 
505*4882a593Smuzhiyun 	audio_io = cygaud->audio;
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 	/*
508*4882a593Smuzhiyun 	 * ESR status gets updates with/without interrupts enabled.
509*4882a593Smuzhiyun 	 * So, check the ESR mask, which provides interrupt enable/
510*4882a593Smuzhiyun 	 * disable status and use it to determine which ESR status
511*4882a593Smuzhiyun 	 * should be serviced.
512*4882a593Smuzhiyun 	 */
513*4882a593Smuzhiyun 	esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET);
514*4882a593Smuzhiyun 	esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET);
515*4882a593Smuzhiyun 	esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET);
516*4882a593Smuzhiyun 	esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET);
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun 	for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) {
519*4882a593Smuzhiyun 		u32 esrmask = BIT(port);
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun 		/*
522*4882a593Smuzhiyun 		 * Ringbuffer or FIFO overflow
523*4882a593Smuzhiyun 		 * If we get this interrupt then, it is also true that we have
524*4882a593Smuzhiyun 		 * not yet responded to the fullmark interrupt.
525*4882a593Smuzhiyun 		 * Log a debug message.  The fullmark handler below will
526*4882a593Smuzhiyun 		 * handle getting everything going again.
527*4882a593Smuzhiyun 		 */
528*4882a593Smuzhiyun 		if (esrmask & esr_status2)
529*4882a593Smuzhiyun 			dev_dbg(cygaud->dev,
530*4882a593Smuzhiyun 				"Overflow: esr2=0x%x\n", esr_status2);
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 		if (esrmask & esr_status4) {
533*4882a593Smuzhiyun 			struct snd_pcm_substream *capstr;
534*4882a593Smuzhiyun 
535*4882a593Smuzhiyun 			capstr = cygaud->portinfo[port].capture_stream;
536*4882a593Smuzhiyun 			cygnus_pcm_period_elapsed(capstr);
537*4882a593Smuzhiyun 		}
538*4882a593Smuzhiyun 	}
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET);
541*4882a593Smuzhiyun 	writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET);
542*4882a593Smuzhiyun 	/* Rearm fullmark logic by writing 1 to the correct bit */
543*4882a593Smuzhiyun 	writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET);
544*4882a593Smuzhiyun }
545*4882a593Smuzhiyun 
cygnus_dma_irq(int irq,void * data)546*4882a593Smuzhiyun static irqreturn_t cygnus_dma_irq(int irq, void *data)
547*4882a593Smuzhiyun {
548*4882a593Smuzhiyun 	u32 r5_status;
549*4882a593Smuzhiyun 	struct cygnus_audio *cygaud = data;
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 	/*
552*4882a593Smuzhiyun 	 * R5 status bits	Description
553*4882a593Smuzhiyun 	 *  0		ESR0 (playback FIFO interrupt)
554*4882a593Smuzhiyun 	 *  1		ESR1 (playback rbuf interrupt)
555*4882a593Smuzhiyun 	 *  2		ESR2 (capture rbuf interrupt)
556*4882a593Smuzhiyun 	 *  3		ESR3 (Freemark play. interrupt)
557*4882a593Smuzhiyun 	 *  4		ESR4 (Fullmark capt. interrupt)
558*4882a593Smuzhiyun 	 */
559*4882a593Smuzhiyun 	r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET);
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ)))
562*4882a593Smuzhiyun 		return IRQ_NONE;
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	/* If playback interrupt happened */
565*4882a593Smuzhiyun 	if (ANY_PLAYBACK_IRQ & r5_status) {
566*4882a593Smuzhiyun 		handle_playback_irq(cygaud);
567*4882a593Smuzhiyun 		writel(ANY_PLAYBACK_IRQ & r5_status,
568*4882a593Smuzhiyun 			cygaud->audio + INTH_R5F_CLEAR_OFFSET);
569*4882a593Smuzhiyun 	}
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun 	/* If  capture interrupt happened */
572*4882a593Smuzhiyun 	if (ANY_CAPTURE_IRQ & r5_status) {
573*4882a593Smuzhiyun 		handle_capture_irq(cygaud);
574*4882a593Smuzhiyun 		writel(ANY_CAPTURE_IRQ & r5_status,
575*4882a593Smuzhiyun 			cygaud->audio + INTH_R5F_CLEAR_OFFSET);
576*4882a593Smuzhiyun 	}
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 	return IRQ_HANDLED;
579*4882a593Smuzhiyun }
580*4882a593Smuzhiyun 
cygnus_pcm_open(struct snd_soc_component * component,struct snd_pcm_substream * substream)581*4882a593Smuzhiyun static int cygnus_pcm_open(struct snd_soc_component *component,
582*4882a593Smuzhiyun 			   struct snd_pcm_substream *substream)
583*4882a593Smuzhiyun {
584*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
585*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
586*4882a593Smuzhiyun 	struct cygnus_aio_port *aio;
587*4882a593Smuzhiyun 	int ret;
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 	aio = cygnus_dai_get_dma_data(substream);
590*4882a593Smuzhiyun 	if (!aio)
591*4882a593Smuzhiyun 		return -ENODEV;
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 	snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw);
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 	ret = snd_pcm_hw_constraint_step(runtime, 0,
598*4882a593Smuzhiyun 		SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN);
599*4882a593Smuzhiyun 	if (ret < 0)
600*4882a593Smuzhiyun 		return ret;
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 	ret = snd_pcm_hw_constraint_step(runtime, 0,
603*4882a593Smuzhiyun 		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN);
604*4882a593Smuzhiyun 	if (ret < 0)
605*4882a593Smuzhiyun 		return ret;
606*4882a593Smuzhiyun 	/*
607*4882a593Smuzhiyun 	 * Keep track of which substream belongs to which port.
608*4882a593Smuzhiyun 	 * This info is needed by snd_pcm_period_elapsed() in irq_handler
609*4882a593Smuzhiyun 	 */
610*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
611*4882a593Smuzhiyun 		aio->play_stream = substream;
612*4882a593Smuzhiyun 	else
613*4882a593Smuzhiyun 		aio->capture_stream = substream;
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	return 0;
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun 
cygnus_pcm_close(struct snd_soc_component * component,struct snd_pcm_substream * substream)618*4882a593Smuzhiyun static int cygnus_pcm_close(struct snd_soc_component *component,
619*4882a593Smuzhiyun 			    struct snd_pcm_substream *substream)
620*4882a593Smuzhiyun {
621*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
622*4882a593Smuzhiyun 	struct cygnus_aio_port *aio;
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun 	aio = cygnus_dai_get_dma_data(substream);
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s  port %d\n", __func__, aio->portnum);
627*4882a593Smuzhiyun 
628*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
629*4882a593Smuzhiyun 		aio->play_stream = NULL;
630*4882a593Smuzhiyun 	else
631*4882a593Smuzhiyun 		aio->capture_stream = NULL;
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 	if (!aio->play_stream && !aio->capture_stream)
634*4882a593Smuzhiyun 		dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "freed  port %d\n", aio->portnum);
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 	return 0;
637*4882a593Smuzhiyun }
638*4882a593Smuzhiyun 
cygnus_pcm_hw_params(struct snd_soc_component * component,struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)639*4882a593Smuzhiyun static int cygnus_pcm_hw_params(struct snd_soc_component *component,
640*4882a593Smuzhiyun 				struct snd_pcm_substream *substream,
641*4882a593Smuzhiyun 				struct snd_pcm_hw_params *params)
642*4882a593Smuzhiyun {
643*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
644*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
645*4882a593Smuzhiyun 	struct cygnus_aio_port *aio;
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun 	aio = cygnus_dai_get_dma_data(substream);
648*4882a593Smuzhiyun 	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s  port %d\n", __func__, aio->portnum);
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun 	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
651*4882a593Smuzhiyun 	runtime->dma_bytes = params_buffer_bytes(params);
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	return 0;
654*4882a593Smuzhiyun }
655*4882a593Smuzhiyun 
cygnus_pcm_hw_free(struct snd_soc_component * component,struct snd_pcm_substream * substream)656*4882a593Smuzhiyun static int cygnus_pcm_hw_free(struct snd_soc_component *component,
657*4882a593Smuzhiyun 			      struct snd_pcm_substream *substream)
658*4882a593Smuzhiyun {
659*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
660*4882a593Smuzhiyun 	struct cygnus_aio_port *aio;
661*4882a593Smuzhiyun 
662*4882a593Smuzhiyun 	aio = cygnus_dai_get_dma_data(substream);
663*4882a593Smuzhiyun 	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s  port %d\n", __func__, aio->portnum);
664*4882a593Smuzhiyun 
665*4882a593Smuzhiyun 	snd_pcm_set_runtime_buffer(substream, NULL);
666*4882a593Smuzhiyun 	return 0;
667*4882a593Smuzhiyun }
668*4882a593Smuzhiyun 
cygnus_pcm_prepare(struct snd_soc_component * component,struct snd_pcm_substream * substream)669*4882a593Smuzhiyun static int cygnus_pcm_prepare(struct snd_soc_component *component,
670*4882a593Smuzhiyun 			      struct snd_pcm_substream *substream)
671*4882a593Smuzhiyun {
672*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
673*4882a593Smuzhiyun 	struct snd_pcm_runtime *runtime = substream->runtime;
674*4882a593Smuzhiyun 	struct cygnus_aio_port *aio;
675*4882a593Smuzhiyun 	unsigned long bufsize, periodsize;
676*4882a593Smuzhiyun 	bool is_play;
677*4882a593Smuzhiyun 	u32 start;
678*4882a593Smuzhiyun 	struct ringbuf_regs *p_rbuf = NULL;
679*4882a593Smuzhiyun 
680*4882a593Smuzhiyun 	aio = cygnus_dai_get_dma_data(substream);
681*4882a593Smuzhiyun 	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s port %d\n", __func__, aio->portnum);
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun 	bufsize = snd_pcm_lib_buffer_bytes(substream);
684*4882a593Smuzhiyun 	periodsize = snd_pcm_lib_period_bytes(substream);
685*4882a593Smuzhiyun 
686*4882a593Smuzhiyun 	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s (buf_size %lu) (period_size %lu)\n",
687*4882a593Smuzhiyun 			__func__, bufsize, periodsize);
688*4882a593Smuzhiyun 
689*4882a593Smuzhiyun 	configure_ringbuf_regs(substream);
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun 	p_rbuf = get_ringbuf(substream);
692*4882a593Smuzhiyun 
693*4882a593Smuzhiyun 	start = runtime->dma_addr;
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 	is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start,
698*4882a593Smuzhiyun 				periodsize, bufsize);
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun 	return 0;
701*4882a593Smuzhiyun }
702*4882a593Smuzhiyun 
cygnus_pcm_pointer(struct snd_soc_component * component,struct snd_pcm_substream * substream)703*4882a593Smuzhiyun static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_soc_component *component,
704*4882a593Smuzhiyun 					    struct snd_pcm_substream *substream)
705*4882a593Smuzhiyun {
706*4882a593Smuzhiyun 	struct cygnus_aio_port *aio;
707*4882a593Smuzhiyun 	unsigned int res = 0, cur = 0, base = 0;
708*4882a593Smuzhiyun 	struct ringbuf_regs *p_rbuf = NULL;
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun 	aio = cygnus_dai_get_dma_data(substream);
711*4882a593Smuzhiyun 
712*4882a593Smuzhiyun 	/*
713*4882a593Smuzhiyun 	 * Get the offset of the current read (for playack) or write
714*4882a593Smuzhiyun 	 * index (for capture).  Report this value back to the asoc framework.
715*4882a593Smuzhiyun 	 */
716*4882a593Smuzhiyun 	p_rbuf = get_ringbuf(substream);
717*4882a593Smuzhiyun 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
718*4882a593Smuzhiyun 		cur = readl(aio->cygaud->audio + p_rbuf->rdaddr);
719*4882a593Smuzhiyun 	else
720*4882a593Smuzhiyun 		cur = readl(aio->cygaud->audio + p_rbuf->wraddr);
721*4882a593Smuzhiyun 
722*4882a593Smuzhiyun 	base = readl(aio->cygaud->audio + p_rbuf->baseaddr);
723*4882a593Smuzhiyun 
724*4882a593Smuzhiyun 	/*
725*4882a593Smuzhiyun 	 * Mask off the MSB of the rdaddr,wraddr and baseaddr
726*4882a593Smuzhiyun 	 * since MSB is not part of the address
727*4882a593Smuzhiyun 	 */
728*4882a593Smuzhiyun 	res = (cur & 0x7fffffff) - (base & 0x7fffffff);
729*4882a593Smuzhiyun 
730*4882a593Smuzhiyun 	return bytes_to_frames(substream->runtime, res);
731*4882a593Smuzhiyun }
732*4882a593Smuzhiyun 
cygnus_pcm_preallocate_dma_buffer(struct snd_pcm * pcm,int stream)733*4882a593Smuzhiyun static int cygnus_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
734*4882a593Smuzhiyun {
735*4882a593Smuzhiyun 	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
736*4882a593Smuzhiyun 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
737*4882a593Smuzhiyun 	struct snd_dma_buffer *buf = &substream->dma_buffer;
738*4882a593Smuzhiyun 	size_t size;
739*4882a593Smuzhiyun 
740*4882a593Smuzhiyun 	size = cygnus_pcm_hw.buffer_bytes_max;
741*4882a593Smuzhiyun 
742*4882a593Smuzhiyun 	buf->dev.type = SNDRV_DMA_TYPE_DEV;
743*4882a593Smuzhiyun 	buf->dev.dev = pcm->card->dev;
744*4882a593Smuzhiyun 	buf->private_data = NULL;
745*4882a593Smuzhiyun 	buf->area = dma_alloc_coherent(pcm->card->dev, size,
746*4882a593Smuzhiyun 			&buf->addr, GFP_KERNEL);
747*4882a593Smuzhiyun 
748*4882a593Smuzhiyun 	dev_dbg(asoc_rtd_to_cpu(rtd, 0)->dev, "%s: size 0x%zx @ %pK\n",
749*4882a593Smuzhiyun 				__func__, size, buf->area);
750*4882a593Smuzhiyun 
751*4882a593Smuzhiyun 	if (!buf->area) {
752*4882a593Smuzhiyun 		dev_err(asoc_rtd_to_cpu(rtd, 0)->dev, "%s: dma_alloc failed\n", __func__);
753*4882a593Smuzhiyun 		return -ENOMEM;
754*4882a593Smuzhiyun 	}
755*4882a593Smuzhiyun 	buf->bytes = size;
756*4882a593Smuzhiyun 
757*4882a593Smuzhiyun 	return 0;
758*4882a593Smuzhiyun }
759*4882a593Smuzhiyun 
cygnus_dma_free_dma_buffers(struct snd_soc_component * component,struct snd_pcm * pcm)760*4882a593Smuzhiyun static void cygnus_dma_free_dma_buffers(struct snd_soc_component *component,
761*4882a593Smuzhiyun 					struct snd_pcm *pcm)
762*4882a593Smuzhiyun {
763*4882a593Smuzhiyun 	struct snd_pcm_substream *substream;
764*4882a593Smuzhiyun 	struct snd_dma_buffer *buf;
765*4882a593Smuzhiyun 
766*4882a593Smuzhiyun 	substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
767*4882a593Smuzhiyun 	if (substream) {
768*4882a593Smuzhiyun 		buf = &substream->dma_buffer;
769*4882a593Smuzhiyun 		if (buf->area) {
770*4882a593Smuzhiyun 			dma_free_coherent(pcm->card->dev, buf->bytes,
771*4882a593Smuzhiyun 				buf->area, buf->addr);
772*4882a593Smuzhiyun 			buf->area = NULL;
773*4882a593Smuzhiyun 		}
774*4882a593Smuzhiyun 	}
775*4882a593Smuzhiyun 
776*4882a593Smuzhiyun 	substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
777*4882a593Smuzhiyun 	if (substream) {
778*4882a593Smuzhiyun 		buf = &substream->dma_buffer;
779*4882a593Smuzhiyun 		if (buf->area) {
780*4882a593Smuzhiyun 			dma_free_coherent(pcm->card->dev, buf->bytes,
781*4882a593Smuzhiyun 				buf->area, buf->addr);
782*4882a593Smuzhiyun 			buf->area = NULL;
783*4882a593Smuzhiyun 		}
784*4882a593Smuzhiyun 	}
785*4882a593Smuzhiyun }
786*4882a593Smuzhiyun 
cygnus_dma_new(struct snd_soc_component * component,struct snd_soc_pcm_runtime * rtd)787*4882a593Smuzhiyun static int cygnus_dma_new(struct snd_soc_component *component,
788*4882a593Smuzhiyun 			  struct snd_soc_pcm_runtime *rtd)
789*4882a593Smuzhiyun {
790*4882a593Smuzhiyun 	struct snd_card *card = rtd->card->snd_card;
791*4882a593Smuzhiyun 	struct snd_pcm *pcm = rtd->pcm;
792*4882a593Smuzhiyun 	int ret;
793*4882a593Smuzhiyun 
794*4882a593Smuzhiyun 	if (!card->dev->dma_mask)
795*4882a593Smuzhiyun 		card->dev->dma_mask = &cygnus_dma_dmamask;
796*4882a593Smuzhiyun 	if (!card->dev->coherent_dma_mask)
797*4882a593Smuzhiyun 		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
798*4882a593Smuzhiyun 
799*4882a593Smuzhiyun 	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
800*4882a593Smuzhiyun 		ret = cygnus_pcm_preallocate_dma_buffer(pcm,
801*4882a593Smuzhiyun 				SNDRV_PCM_STREAM_PLAYBACK);
802*4882a593Smuzhiyun 		if (ret)
803*4882a593Smuzhiyun 			return ret;
804*4882a593Smuzhiyun 	}
805*4882a593Smuzhiyun 
806*4882a593Smuzhiyun 	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
807*4882a593Smuzhiyun 		ret = cygnus_pcm_preallocate_dma_buffer(pcm,
808*4882a593Smuzhiyun 				SNDRV_PCM_STREAM_CAPTURE);
809*4882a593Smuzhiyun 		if (ret) {
810*4882a593Smuzhiyun 			cygnus_dma_free_dma_buffers(component, pcm);
811*4882a593Smuzhiyun 			return ret;
812*4882a593Smuzhiyun 		}
813*4882a593Smuzhiyun 	}
814*4882a593Smuzhiyun 
815*4882a593Smuzhiyun 	return 0;
816*4882a593Smuzhiyun }
817*4882a593Smuzhiyun 
818*4882a593Smuzhiyun static struct snd_soc_component_driver cygnus_soc_platform = {
819*4882a593Smuzhiyun 	.open		= cygnus_pcm_open,
820*4882a593Smuzhiyun 	.close		= cygnus_pcm_close,
821*4882a593Smuzhiyun 	.hw_params	= cygnus_pcm_hw_params,
822*4882a593Smuzhiyun 	.hw_free	= cygnus_pcm_hw_free,
823*4882a593Smuzhiyun 	.prepare	= cygnus_pcm_prepare,
824*4882a593Smuzhiyun 	.trigger	= cygnus_pcm_trigger,
825*4882a593Smuzhiyun 	.pointer	= cygnus_pcm_pointer,
826*4882a593Smuzhiyun 	.pcm_construct	= cygnus_dma_new,
827*4882a593Smuzhiyun 	.pcm_destruct	= cygnus_dma_free_dma_buffers,
828*4882a593Smuzhiyun };
829*4882a593Smuzhiyun 
cygnus_soc_platform_register(struct device * dev,struct cygnus_audio * cygaud)830*4882a593Smuzhiyun int cygnus_soc_platform_register(struct device *dev,
831*4882a593Smuzhiyun 				 struct cygnus_audio *cygaud)
832*4882a593Smuzhiyun {
833*4882a593Smuzhiyun 	int rc = 0;
834*4882a593Smuzhiyun 
835*4882a593Smuzhiyun 	dev_dbg(dev, "%s Enter\n", __func__);
836*4882a593Smuzhiyun 
837*4882a593Smuzhiyun 	rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq,
838*4882a593Smuzhiyun 				IRQF_SHARED, "cygnus-audio", cygaud);
839*4882a593Smuzhiyun 	if (rc) {
840*4882a593Smuzhiyun 		dev_err(dev, "%s request_irq error %d\n", __func__, rc);
841*4882a593Smuzhiyun 		return rc;
842*4882a593Smuzhiyun 	}
843*4882a593Smuzhiyun 
844*4882a593Smuzhiyun 	rc = devm_snd_soc_register_component(dev, &cygnus_soc_platform,
845*4882a593Smuzhiyun 					     NULL, 0);
846*4882a593Smuzhiyun 	if (rc) {
847*4882a593Smuzhiyun 		dev_err(dev, "%s failed\n", __func__);
848*4882a593Smuzhiyun 		return rc;
849*4882a593Smuzhiyun 	}
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun 	return 0;
852*4882a593Smuzhiyun }
853*4882a593Smuzhiyun 
cygnus_soc_platform_unregister(struct device * dev)854*4882a593Smuzhiyun int cygnus_soc_platform_unregister(struct device *dev)
855*4882a593Smuzhiyun {
856*4882a593Smuzhiyun 	return 0;
857*4882a593Smuzhiyun }
858*4882a593Smuzhiyun 
859*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
860*4882a593Smuzhiyun MODULE_AUTHOR("Broadcom");
861*4882a593Smuzhiyun MODULE_DESCRIPTION("Cygnus ASoC PCM module");
862