xref: /OK3568_Linux_fs/kernel/sound/soc/sh/siu_pcm.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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 = &param->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