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