1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun //
3*4882a593Smuzhiyun // siu_pcm.c - ALSA driver for Renesas SH7343, SH7722 SIU peripheral.
4*4882a593Smuzhiyun //
5*4882a593Smuzhiyun // Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
6*4882a593Smuzhiyun // Copyright (C) 2006 Carlos Munoz <carlos@kenati.com>
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/delay.h>
9*4882a593Smuzhiyun #include <linux/dma-mapping.h>
10*4882a593Smuzhiyun #include <linux/dmaengine.h>
11*4882a593Smuzhiyun #include <linux/interrupt.h>
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/platform_device.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <sound/control.h>
16*4882a593Smuzhiyun #include <sound/core.h>
17*4882a593Smuzhiyun #include <sound/pcm.h>
18*4882a593Smuzhiyun #include <sound/pcm_params.h>
19*4882a593Smuzhiyun #include <sound/soc.h>
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun #include <asm/siu.h>
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #include "siu.h"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #define DRV_NAME "siu-i2s"
26*4882a593Smuzhiyun #define GET_MAX_PERIODS(buf_bytes, period_bytes) \
27*4882a593Smuzhiyun ((buf_bytes) / (period_bytes))
28*4882a593Smuzhiyun #define PERIOD_OFFSET(buf_addr, period_num, period_bytes) \
29*4882a593Smuzhiyun ((buf_addr) + ((period_num) * (period_bytes)))
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun #define RWF_STM_RD 0x01 /* Read in progress */
32*4882a593Smuzhiyun #define RWF_STM_WT 0x02 /* Write in progress */
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun struct siu_port *siu_ports[SIU_PORT_NUM];
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun /* transfersize is number of u32 dma transfers per period */
siu_pcm_stmwrite_stop(struct siu_port * port_info)37*4882a593Smuzhiyun static int siu_pcm_stmwrite_stop(struct siu_port *port_info)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun struct siu_info *info = siu_i2s_data;
40*4882a593Smuzhiyun u32 __iomem *base = info->reg;
41*4882a593Smuzhiyun struct siu_stream *siu_stream = &port_info->playback;
42*4882a593Smuzhiyun u32 stfifo;
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun if (!siu_stream->rw_flg)
45*4882a593Smuzhiyun return -EPERM;
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun /* output FIFO disable */
48*4882a593Smuzhiyun stfifo = siu_read32(base + SIU_STFIFO);
49*4882a593Smuzhiyun siu_write32(base + SIU_STFIFO, stfifo & ~0x0c180c18);
50*4882a593Smuzhiyun pr_debug("%s: STFIFO %x -> %x\n", __func__,
51*4882a593Smuzhiyun stfifo, stfifo & ~0x0c180c18);
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun /* during stmwrite clear */
54*4882a593Smuzhiyun siu_stream->rw_flg = 0;
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun return 0;
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun
siu_pcm_stmwrite_start(struct siu_port * port_info)59*4882a593Smuzhiyun static int siu_pcm_stmwrite_start(struct siu_port *port_info)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun struct siu_stream *siu_stream = &port_info->playback;
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun if (siu_stream->rw_flg)
64*4882a593Smuzhiyun return -EPERM;
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun /* Current period in buffer */
67*4882a593Smuzhiyun port_info->playback.cur_period = 0;
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun /* during stmwrite flag set */
70*4882a593Smuzhiyun siu_stream->rw_flg = RWF_STM_WT;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun /* DMA transfer start */
73*4882a593Smuzhiyun queue_work(system_highpri_wq, &siu_stream->work);
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun return 0;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun
siu_dma_tx_complete(void * arg)78*4882a593Smuzhiyun static void siu_dma_tx_complete(void *arg)
79*4882a593Smuzhiyun {
80*4882a593Smuzhiyun struct siu_stream *siu_stream = arg;
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun if (!siu_stream->rw_flg)
83*4882a593Smuzhiyun return;
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun /* Update completed period count */
86*4882a593Smuzhiyun if (++siu_stream->cur_period >=
87*4882a593Smuzhiyun GET_MAX_PERIODS(siu_stream->buf_bytes,
88*4882a593Smuzhiyun siu_stream->period_bytes))
89*4882a593Smuzhiyun siu_stream->cur_period = 0;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun pr_debug("%s: done period #%d (%u/%u bytes), cookie %d\n",
92*4882a593Smuzhiyun __func__, siu_stream->cur_period,
93*4882a593Smuzhiyun siu_stream->cur_period * siu_stream->period_bytes,
94*4882a593Smuzhiyun siu_stream->buf_bytes, siu_stream->cookie);
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun queue_work(system_highpri_wq, &siu_stream->work);
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun /* Notify alsa: a period is done */
99*4882a593Smuzhiyun snd_pcm_period_elapsed(siu_stream->substream);
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun
siu_pcm_wr_set(struct siu_port * port_info,dma_addr_t buff,u32 size)102*4882a593Smuzhiyun static int siu_pcm_wr_set(struct siu_port *port_info,
103*4882a593Smuzhiyun dma_addr_t buff, u32 size)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun struct siu_info *info = siu_i2s_data;
106*4882a593Smuzhiyun u32 __iomem *base = info->reg;
107*4882a593Smuzhiyun struct siu_stream *siu_stream = &port_info->playback;
108*4882a593Smuzhiyun struct snd_pcm_substream *substream = siu_stream->substream;
109*4882a593Smuzhiyun struct device *dev = substream->pcm->card->dev;
110*4882a593Smuzhiyun struct dma_async_tx_descriptor *desc;
111*4882a593Smuzhiyun dma_cookie_t cookie;
112*4882a593Smuzhiyun struct scatterlist sg;
113*4882a593Smuzhiyun u32 stfifo;
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun sg_init_table(&sg, 1);
116*4882a593Smuzhiyun sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)),
117*4882a593Smuzhiyun size, offset_in_page(buff));
118*4882a593Smuzhiyun sg_dma_len(&sg) = size;
119*4882a593Smuzhiyun sg_dma_address(&sg) = buff;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun desc = dmaengine_prep_slave_sg(siu_stream->chan,
122*4882a593Smuzhiyun &sg, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
123*4882a593Smuzhiyun if (!desc) {
124*4882a593Smuzhiyun dev_err(dev, "Failed to allocate a dma descriptor\n");
125*4882a593Smuzhiyun return -ENOMEM;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun desc->callback = siu_dma_tx_complete;
129*4882a593Smuzhiyun desc->callback_param = siu_stream;
130*4882a593Smuzhiyun cookie = dmaengine_submit(desc);
131*4882a593Smuzhiyun if (cookie < 0) {
132*4882a593Smuzhiyun dev_err(dev, "Failed to submit a dma transfer\n");
133*4882a593Smuzhiyun return cookie;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun siu_stream->tx_desc = desc;
137*4882a593Smuzhiyun siu_stream->cookie = cookie;
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun dma_async_issue_pending(siu_stream->chan);
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun /* only output FIFO enable */
142*4882a593Smuzhiyun stfifo = siu_read32(base + SIU_STFIFO);
143*4882a593Smuzhiyun siu_write32(base + SIU_STFIFO, stfifo | (port_info->stfifo & 0x0c180c18));
144*4882a593Smuzhiyun dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
145*4882a593Smuzhiyun stfifo, stfifo | (port_info->stfifo & 0x0c180c18));
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun return 0;
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun
siu_pcm_rd_set(struct siu_port * port_info,dma_addr_t buff,size_t size)150*4882a593Smuzhiyun static int siu_pcm_rd_set(struct siu_port *port_info,
151*4882a593Smuzhiyun dma_addr_t buff, size_t size)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun struct siu_info *info = siu_i2s_data;
154*4882a593Smuzhiyun u32 __iomem *base = info->reg;
155*4882a593Smuzhiyun struct siu_stream *siu_stream = &port_info->capture;
156*4882a593Smuzhiyun struct snd_pcm_substream *substream = siu_stream->substream;
157*4882a593Smuzhiyun struct device *dev = substream->pcm->card->dev;
158*4882a593Smuzhiyun struct dma_async_tx_descriptor *desc;
159*4882a593Smuzhiyun dma_cookie_t cookie;
160*4882a593Smuzhiyun struct scatterlist sg;
161*4882a593Smuzhiyun u32 stfifo;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun dev_dbg(dev, "%s: %u@%llx\n", __func__, size, (unsigned long long)buff);
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun sg_init_table(&sg, 1);
166*4882a593Smuzhiyun sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)),
167*4882a593Smuzhiyun size, offset_in_page(buff));
168*4882a593Smuzhiyun sg_dma_len(&sg) = size;
169*4882a593Smuzhiyun sg_dma_address(&sg) = buff;
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun desc = dmaengine_prep_slave_sg(siu_stream->chan,
172*4882a593Smuzhiyun &sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
173*4882a593Smuzhiyun if (!desc) {
174*4882a593Smuzhiyun dev_err(dev, "Failed to allocate dma descriptor\n");
175*4882a593Smuzhiyun return -ENOMEM;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun desc->callback = siu_dma_tx_complete;
179*4882a593Smuzhiyun desc->callback_param = siu_stream;
180*4882a593Smuzhiyun cookie = dmaengine_submit(desc);
181*4882a593Smuzhiyun if (cookie < 0) {
182*4882a593Smuzhiyun dev_err(dev, "Failed to submit dma descriptor\n");
183*4882a593Smuzhiyun return cookie;
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun siu_stream->tx_desc = desc;
187*4882a593Smuzhiyun siu_stream->cookie = cookie;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun dma_async_issue_pending(siu_stream->chan);
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun /* only input FIFO enable */
192*4882a593Smuzhiyun stfifo = siu_read32(base + SIU_STFIFO);
193*4882a593Smuzhiyun siu_write32(base + SIU_STFIFO, siu_read32(base + SIU_STFIFO) |
194*4882a593Smuzhiyun (port_info->stfifo & 0x13071307));
195*4882a593Smuzhiyun dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
196*4882a593Smuzhiyun stfifo, stfifo | (port_info->stfifo & 0x13071307));
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun return 0;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun
siu_io_work(struct work_struct * work)201*4882a593Smuzhiyun static void siu_io_work(struct work_struct *work)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun struct siu_stream *siu_stream = container_of(work, struct siu_stream,
204*4882a593Smuzhiyun work);
205*4882a593Smuzhiyun struct snd_pcm_substream *substream = siu_stream->substream;
206*4882a593Smuzhiyun struct device *dev = substream->pcm->card->dev;
207*4882a593Smuzhiyun struct snd_pcm_runtime *rt = substream->runtime;
208*4882a593Smuzhiyun struct siu_port *port_info = siu_port_info(substream);
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun dev_dbg(dev, "%s: flags %x\n", __func__, siu_stream->rw_flg);
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun if (!siu_stream->rw_flg) {
213*4882a593Smuzhiyun dev_dbg(dev, "%s: stream inactive\n", __func__);
214*4882a593Smuzhiyun return;
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
218*4882a593Smuzhiyun dma_addr_t buff;
219*4882a593Smuzhiyun size_t count;
220*4882a593Smuzhiyun u8 *virt;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun buff = (dma_addr_t)PERIOD_OFFSET(rt->dma_addr,
223*4882a593Smuzhiyun siu_stream->cur_period,
224*4882a593Smuzhiyun siu_stream->period_bytes);
225*4882a593Smuzhiyun virt = PERIOD_OFFSET(rt->dma_area,
226*4882a593Smuzhiyun siu_stream->cur_period,
227*4882a593Smuzhiyun siu_stream->period_bytes);
228*4882a593Smuzhiyun count = siu_stream->period_bytes;
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun /* DMA transfer start */
231*4882a593Smuzhiyun siu_pcm_rd_set(port_info, buff, count);
232*4882a593Smuzhiyun } else {
233*4882a593Smuzhiyun siu_pcm_wr_set(port_info,
234*4882a593Smuzhiyun (dma_addr_t)PERIOD_OFFSET(rt->dma_addr,
235*4882a593Smuzhiyun siu_stream->cur_period,
236*4882a593Smuzhiyun siu_stream->period_bytes),
237*4882a593Smuzhiyun siu_stream->period_bytes);
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun /* Capture */
siu_pcm_stmread_start(struct siu_port * port_info)242*4882a593Smuzhiyun static int siu_pcm_stmread_start(struct siu_port *port_info)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun struct siu_stream *siu_stream = &port_info->capture;
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun if (siu_stream->xfer_cnt > 0x1000000)
247*4882a593Smuzhiyun return -EINVAL;
248*4882a593Smuzhiyun if (siu_stream->rw_flg)
249*4882a593Smuzhiyun return -EPERM;
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun /* Current period in buffer */
252*4882a593Smuzhiyun siu_stream->cur_period = 0;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun /* during stmread flag set */
255*4882a593Smuzhiyun siu_stream->rw_flg = RWF_STM_RD;
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun queue_work(system_highpri_wq, &siu_stream->work);
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun return 0;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun
siu_pcm_stmread_stop(struct siu_port * port_info)262*4882a593Smuzhiyun static int siu_pcm_stmread_stop(struct siu_port *port_info)
263*4882a593Smuzhiyun {
264*4882a593Smuzhiyun struct siu_info *info = siu_i2s_data;
265*4882a593Smuzhiyun u32 __iomem *base = info->reg;
266*4882a593Smuzhiyun struct siu_stream *siu_stream = &port_info->capture;
267*4882a593Smuzhiyun struct device *dev = siu_stream->substream->pcm->card->dev;
268*4882a593Smuzhiyun u32 stfifo;
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun if (!siu_stream->rw_flg)
271*4882a593Smuzhiyun return -EPERM;
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun /* input FIFO disable */
274*4882a593Smuzhiyun stfifo = siu_read32(base + SIU_STFIFO);
275*4882a593Smuzhiyun siu_write32(base + SIU_STFIFO, stfifo & ~0x13071307);
276*4882a593Smuzhiyun dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
277*4882a593Smuzhiyun stfifo, stfifo & ~0x13071307);
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun /* during stmread flag clear */
280*4882a593Smuzhiyun siu_stream->rw_flg = 0;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun return 0;
283*4882a593Smuzhiyun }
284*4882a593Smuzhiyun
filter(struct dma_chan * chan,void * secondary)285*4882a593Smuzhiyun static bool filter(struct dma_chan *chan, void *secondary)
286*4882a593Smuzhiyun {
287*4882a593Smuzhiyun struct sh_dmae_slave *param = secondary;
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun pr_debug("%s: secondary ID %d\n", __func__, param->shdma_slave.slave_id);
290*4882a593Smuzhiyun
291*4882a593Smuzhiyun chan->private = ¶m->shdma_slave;
292*4882a593Smuzhiyun return true;
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun
siu_pcm_open(struct snd_soc_component * component,struct snd_pcm_substream * ss)295*4882a593Smuzhiyun static int siu_pcm_open(struct snd_soc_component *component,
296*4882a593Smuzhiyun struct snd_pcm_substream *ss)
297*4882a593Smuzhiyun {
298*4882a593Smuzhiyun /* Playback / Capture */
299*4882a593Smuzhiyun struct siu_platform *pdata = component->dev->platform_data;
300*4882a593Smuzhiyun struct siu_info *info = siu_i2s_data;
301*4882a593Smuzhiyun struct siu_port *port_info = siu_port_info(ss);
302*4882a593Smuzhiyun struct siu_stream *siu_stream;
303*4882a593Smuzhiyun u32 port = info->port_id;
304*4882a593Smuzhiyun struct device *dev = ss->pcm->card->dev;
305*4882a593Smuzhiyun dma_cap_mask_t mask;
306*4882a593Smuzhiyun struct sh_dmae_slave *param;
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun dma_cap_zero(mask);
309*4882a593Smuzhiyun dma_cap_set(DMA_SLAVE, mask);
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun dev_dbg(dev, "%s, port=%d@%p\n", __func__, port, port_info);
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) {
314*4882a593Smuzhiyun siu_stream = &port_info->playback;
315*4882a593Smuzhiyun param = &siu_stream->param;
316*4882a593Smuzhiyun param->shdma_slave.slave_id = port ? pdata->dma_slave_tx_b :
317*4882a593Smuzhiyun pdata->dma_slave_tx_a;
318*4882a593Smuzhiyun } else {
319*4882a593Smuzhiyun siu_stream = &port_info->capture;
320*4882a593Smuzhiyun param = &siu_stream->param;
321*4882a593Smuzhiyun param->shdma_slave.slave_id = port ? pdata->dma_slave_rx_b :
322*4882a593Smuzhiyun pdata->dma_slave_rx_a;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun /* Get DMA channel */
326*4882a593Smuzhiyun siu_stream->chan = dma_request_channel(mask, filter, param);
327*4882a593Smuzhiyun if (!siu_stream->chan) {
328*4882a593Smuzhiyun dev_err(dev, "DMA channel allocation failed!\n");
329*4882a593Smuzhiyun return -EBUSY;
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun siu_stream->substream = ss;
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun return 0;
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun
siu_pcm_close(struct snd_soc_component * component,struct snd_pcm_substream * ss)337*4882a593Smuzhiyun static int siu_pcm_close(struct snd_soc_component *component,
338*4882a593Smuzhiyun struct snd_pcm_substream *ss)
339*4882a593Smuzhiyun {
340*4882a593Smuzhiyun struct siu_info *info = siu_i2s_data;
341*4882a593Smuzhiyun struct device *dev = ss->pcm->card->dev;
342*4882a593Smuzhiyun struct siu_port *port_info = siu_port_info(ss);
343*4882a593Smuzhiyun struct siu_stream *siu_stream;
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id);
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
348*4882a593Smuzhiyun siu_stream = &port_info->playback;
349*4882a593Smuzhiyun else
350*4882a593Smuzhiyun siu_stream = &port_info->capture;
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun dma_release_channel(siu_stream->chan);
353*4882a593Smuzhiyun siu_stream->chan = NULL;
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun siu_stream->substream = NULL;
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun return 0;
358*4882a593Smuzhiyun }
359*4882a593Smuzhiyun
siu_pcm_prepare(struct snd_soc_component * component,struct snd_pcm_substream * ss)360*4882a593Smuzhiyun static int siu_pcm_prepare(struct snd_soc_component *component,
361*4882a593Smuzhiyun struct snd_pcm_substream *ss)
362*4882a593Smuzhiyun {
363*4882a593Smuzhiyun struct siu_info *info = siu_i2s_data;
364*4882a593Smuzhiyun struct siu_port *port_info = siu_port_info(ss);
365*4882a593Smuzhiyun struct device *dev = ss->pcm->card->dev;
366*4882a593Smuzhiyun struct snd_pcm_runtime *rt = ss->runtime;
367*4882a593Smuzhiyun struct siu_stream *siu_stream;
368*4882a593Smuzhiyun snd_pcm_sframes_t xfer_cnt;
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
371*4882a593Smuzhiyun siu_stream = &port_info->playback;
372*4882a593Smuzhiyun else
373*4882a593Smuzhiyun siu_stream = &port_info->capture;
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun rt = siu_stream->substream->runtime;
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun siu_stream->buf_bytes = snd_pcm_lib_buffer_bytes(ss);
378*4882a593Smuzhiyun siu_stream->period_bytes = snd_pcm_lib_period_bytes(ss);
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun dev_dbg(dev, "%s: port=%d, %d channels, period=%u bytes\n", __func__,
381*4882a593Smuzhiyun info->port_id, rt->channels, siu_stream->period_bytes);
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun /* We only support buffers that are multiples of the period */
384*4882a593Smuzhiyun if (siu_stream->buf_bytes % siu_stream->period_bytes) {
385*4882a593Smuzhiyun dev_err(dev, "%s() - buffer=%d not multiple of period=%d\n",
386*4882a593Smuzhiyun __func__, siu_stream->buf_bytes,
387*4882a593Smuzhiyun siu_stream->period_bytes);
388*4882a593Smuzhiyun return -EINVAL;
389*4882a593Smuzhiyun }
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun xfer_cnt = bytes_to_frames(rt, siu_stream->period_bytes);
392*4882a593Smuzhiyun if (!xfer_cnt || xfer_cnt > 0x1000000)
393*4882a593Smuzhiyun return -EINVAL;
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun siu_stream->format = rt->format;
396*4882a593Smuzhiyun siu_stream->xfer_cnt = xfer_cnt;
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun dev_dbg(dev, "port=%d buf=%lx buf_bytes=%d period_bytes=%d "
399*4882a593Smuzhiyun "format=%d channels=%d xfer_cnt=%d\n", info->port_id,
400*4882a593Smuzhiyun (unsigned long)rt->dma_addr, siu_stream->buf_bytes,
401*4882a593Smuzhiyun siu_stream->period_bytes,
402*4882a593Smuzhiyun siu_stream->format, rt->channels, (int)xfer_cnt);
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun return 0;
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun
siu_pcm_trigger(struct snd_soc_component * component,struct snd_pcm_substream * ss,int cmd)407*4882a593Smuzhiyun static int siu_pcm_trigger(struct snd_soc_component *component,
408*4882a593Smuzhiyun struct snd_pcm_substream *ss, int cmd)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun struct siu_info *info = siu_i2s_data;
411*4882a593Smuzhiyun struct device *dev = ss->pcm->card->dev;
412*4882a593Smuzhiyun struct siu_port *port_info = siu_port_info(ss);
413*4882a593Smuzhiyun int ret;
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun dev_dbg(dev, "%s: port=%d@%p, cmd=%d\n", __func__,
416*4882a593Smuzhiyun info->port_id, port_info, cmd);
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun switch (cmd) {
419*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_START:
420*4882a593Smuzhiyun if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
421*4882a593Smuzhiyun ret = siu_pcm_stmwrite_start(port_info);
422*4882a593Smuzhiyun else
423*4882a593Smuzhiyun ret = siu_pcm_stmread_start(port_info);
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun if (ret < 0)
426*4882a593Smuzhiyun dev_warn(dev, "%s: start failed on port=%d\n",
427*4882a593Smuzhiyun __func__, info->port_id);
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun break;
430*4882a593Smuzhiyun case SNDRV_PCM_TRIGGER_STOP:
431*4882a593Smuzhiyun if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
432*4882a593Smuzhiyun siu_pcm_stmwrite_stop(port_info);
433*4882a593Smuzhiyun else
434*4882a593Smuzhiyun siu_pcm_stmread_stop(port_info);
435*4882a593Smuzhiyun ret = 0;
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun break;
438*4882a593Smuzhiyun default:
439*4882a593Smuzhiyun dev_err(dev, "%s() unsupported cmd=%d\n", __func__, cmd);
440*4882a593Smuzhiyun ret = -EINVAL;
441*4882a593Smuzhiyun }
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun return ret;
444*4882a593Smuzhiyun }
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun /*
447*4882a593Smuzhiyun * So far only resolution of one period is supported, subject to extending the
448*4882a593Smuzhiyun * dmangine API
449*4882a593Smuzhiyun */
450*4882a593Smuzhiyun static snd_pcm_uframes_t
siu_pcm_pointer_dma(struct snd_soc_component * component,struct snd_pcm_substream * ss)451*4882a593Smuzhiyun siu_pcm_pointer_dma(struct snd_soc_component *component,
452*4882a593Smuzhiyun struct snd_pcm_substream *ss)
453*4882a593Smuzhiyun {
454*4882a593Smuzhiyun struct device *dev = ss->pcm->card->dev;
455*4882a593Smuzhiyun struct siu_info *info = siu_i2s_data;
456*4882a593Smuzhiyun u32 __iomem *base = info->reg;
457*4882a593Smuzhiyun struct siu_port *port_info = siu_port_info(ss);
458*4882a593Smuzhiyun struct snd_pcm_runtime *rt = ss->runtime;
459*4882a593Smuzhiyun size_t ptr;
460*4882a593Smuzhiyun struct siu_stream *siu_stream;
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
463*4882a593Smuzhiyun siu_stream = &port_info->playback;
464*4882a593Smuzhiyun else
465*4882a593Smuzhiyun siu_stream = &port_info->capture;
466*4882a593Smuzhiyun
467*4882a593Smuzhiyun /*
468*4882a593Smuzhiyun * ptr is the offset into the buffer where the dma is currently at. We
469*4882a593Smuzhiyun * check if the dma buffer has just wrapped.
470*4882a593Smuzhiyun */
471*4882a593Smuzhiyun ptr = PERIOD_OFFSET(rt->dma_addr,
472*4882a593Smuzhiyun siu_stream->cur_period,
473*4882a593Smuzhiyun siu_stream->period_bytes) - rt->dma_addr;
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun dev_dbg(dev,
476*4882a593Smuzhiyun "%s: port=%d, events %x, FSTS %x, xferred %u/%u, cookie %d\n",
477*4882a593Smuzhiyun __func__, info->port_id, siu_read32(base + SIU_EVNTC),
478*4882a593Smuzhiyun siu_read32(base + SIU_SBFSTS), ptr, siu_stream->buf_bytes,
479*4882a593Smuzhiyun siu_stream->cookie);
480*4882a593Smuzhiyun
481*4882a593Smuzhiyun if (ptr >= siu_stream->buf_bytes)
482*4882a593Smuzhiyun ptr = 0;
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun return bytes_to_frames(ss->runtime, ptr);
485*4882a593Smuzhiyun }
486*4882a593Smuzhiyun
siu_pcm_new(struct snd_soc_component * component,struct snd_soc_pcm_runtime * rtd)487*4882a593Smuzhiyun static int siu_pcm_new(struct snd_soc_component *component,
488*4882a593Smuzhiyun struct snd_soc_pcm_runtime *rtd)
489*4882a593Smuzhiyun {
490*4882a593Smuzhiyun /* card->dev == socdev->dev, see snd_soc_new_pcms() */
491*4882a593Smuzhiyun struct snd_card *card = rtd->card->snd_card;
492*4882a593Smuzhiyun struct snd_pcm *pcm = rtd->pcm;
493*4882a593Smuzhiyun struct siu_info *info = siu_i2s_data;
494*4882a593Smuzhiyun struct platform_device *pdev = to_platform_device(card->dev);
495*4882a593Smuzhiyun int ret;
496*4882a593Smuzhiyun int i;
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun /* pdev->id selects between SIUA and SIUB */
499*4882a593Smuzhiyun if (pdev->id < 0 || pdev->id >= SIU_PORT_NUM)
500*4882a593Smuzhiyun return -EINVAL;
501*4882a593Smuzhiyun
502*4882a593Smuzhiyun info->port_id = pdev->id;
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun /*
505*4882a593Smuzhiyun * While the siu has 2 ports, only one port can be on at a time (only 1
506*4882a593Smuzhiyun * SPB). So far all the boards using the siu had only one of the ports
507*4882a593Smuzhiyun * wired to a codec. To simplify things, we only register one port with
508*4882a593Smuzhiyun * alsa. In case both ports are needed, it should be changed here
509*4882a593Smuzhiyun */
510*4882a593Smuzhiyun for (i = pdev->id; i < pdev->id + 1; i++) {
511*4882a593Smuzhiyun struct siu_port **port_info = &siu_ports[i];
512*4882a593Smuzhiyun
513*4882a593Smuzhiyun ret = siu_init_port(i, port_info, card);
514*4882a593Smuzhiyun if (ret < 0)
515*4882a593Smuzhiyun return ret;
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun snd_pcm_set_managed_buffer_all(pcm,
518*4882a593Smuzhiyun SNDRV_DMA_TYPE_DEV, card->dev,
519*4882a593Smuzhiyun SIU_BUFFER_BYTES_MAX, SIU_BUFFER_BYTES_MAX);
520*4882a593Smuzhiyun
521*4882a593Smuzhiyun (*port_info)->pcm = pcm;
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun /* IO works */
524*4882a593Smuzhiyun INIT_WORK(&(*port_info)->playback.work, siu_io_work);
525*4882a593Smuzhiyun INIT_WORK(&(*port_info)->capture.work, siu_io_work);
526*4882a593Smuzhiyun }
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun dev_info(card->dev, "SuperH SIU driver initialized.\n");
529*4882a593Smuzhiyun return 0;
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun
siu_pcm_free(struct snd_soc_component * component,struct snd_pcm * pcm)532*4882a593Smuzhiyun static void siu_pcm_free(struct snd_soc_component *component,
533*4882a593Smuzhiyun struct snd_pcm *pcm)
534*4882a593Smuzhiyun {
535*4882a593Smuzhiyun struct platform_device *pdev = to_platform_device(pcm->card->dev);
536*4882a593Smuzhiyun struct siu_port *port_info = siu_ports[pdev->id];
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun cancel_work_sync(&port_info->capture.work);
539*4882a593Smuzhiyun cancel_work_sync(&port_info->playback.work);
540*4882a593Smuzhiyun
541*4882a593Smuzhiyun siu_free_port(port_info);
542*4882a593Smuzhiyun
543*4882a593Smuzhiyun dev_dbg(pcm->card->dev, "%s\n", __func__);
544*4882a593Smuzhiyun }
545*4882a593Smuzhiyun
546*4882a593Smuzhiyun const struct snd_soc_component_driver siu_component = {
547*4882a593Smuzhiyun .name = DRV_NAME,
548*4882a593Smuzhiyun .open = siu_pcm_open,
549*4882a593Smuzhiyun .close = siu_pcm_close,
550*4882a593Smuzhiyun .prepare = siu_pcm_prepare,
551*4882a593Smuzhiyun .trigger = siu_pcm_trigger,
552*4882a593Smuzhiyun .pointer = siu_pcm_pointer_dma,
553*4882a593Smuzhiyun .pcm_construct = siu_pcm_new,
554*4882a593Smuzhiyun .pcm_destruct = siu_pcm_free,
555*4882a593Smuzhiyun };
556*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(siu_component);
557