1 /*
2  *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
3  *
4  *  This program is free software; you can redistribute	 it and/or modify it
5  *  under  the terms of	 the GNU General  Public License as published by the
6  *  Free Software Foundation;  either version 2 of the	License, or (at your
7  *  option) any later version.
8  *
9  *  You should have received a copy of the  GNU General Public License along
10  *  with this program; if not, write  to the Free Software Foundation, Inc.,
11  *  675 Mass Ave, Cambridge, MA 02139, USA.
12  *
13  */
14 
15 #include <linux/init.h>
16 #include <linux/interrupt.h>
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/platform_device.h>
20 #include <linux/slab.h>
21 
22 #include <linux/dma-mapping.h>
23 
24 #include <sound/core.h>
25 #include <sound/pcm.h>
26 #include <sound/pcm_params.h>
27 #include <sound/soc.h>
28 
29 #include <asm/mach-jz4740/dma.h>
30 #include "jz4740-pcm.h"
31 
32 struct jz4740_runtime_data {
33 	unsigned long dma_period;
34 	dma_addr_t dma_start;
35 	dma_addr_t dma_pos;
36 	dma_addr_t dma_end;
37 
38 	struct jz4740_dma_chan *dma;
39 
40 	dma_addr_t fifo_addr;
41 };
42 
43 /* identify hardware playback capabilities */
44 static const struct snd_pcm_hardware jz4740_pcm_hardware = {
45 	.info = SNDRV_PCM_INFO_MMAP |
46 		SNDRV_PCM_INFO_MMAP_VALID |
47 		SNDRV_PCM_INFO_INTERLEAVED |
48 		SNDRV_PCM_INFO_BLOCK_TRANSFER,
49 	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
50 
51 	.rates			= SNDRV_PCM_RATE_8000_48000,
52 	.channels_min		= 1,
53 	.channels_max		= 2,
54 	.period_bytes_min	= 16,
55 	.period_bytes_max	= 2 * PAGE_SIZE,
56 	.periods_min		= 2,
57 	.periods_max		= 128,
58 	.buffer_bytes_max	= 128 * 2 * PAGE_SIZE,
59 	.fifo_size		= 32,
60 };
61 
jz4740_pcm_start_transfer(struct jz4740_runtime_data * prtd,struct snd_pcm_substream * substream)62 static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd,
63 	struct snd_pcm_substream *substream)
64 {
65 	unsigned long count;
66 
67 	if (prtd->dma_pos == prtd->dma_end)
68 		prtd->dma_pos = prtd->dma_start;
69 
70 	if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
71 		count = prtd->dma_end - prtd->dma_pos;
72 	else
73 		count = prtd->dma_period;
74 
75 	jz4740_dma_disable(prtd->dma);
76 
77 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
78 		jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos);
79 		jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr);
80 	} else {
81 		jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr);
82 		jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos);
83 	}
84 
85 	jz4740_dma_set_transfer_count(prtd->dma, count);
86 
87 	prtd->dma_pos += count;
88 
89 	jz4740_dma_enable(prtd->dma);
90 }
91 
jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan * dma,int err,void * dev_id)92 static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
93 	void *dev_id)
94 {
95 	struct snd_pcm_substream *substream = dev_id;
96 	struct snd_pcm_runtime *runtime = substream->runtime;
97 	struct jz4740_runtime_data *prtd = runtime->private_data;
98 
99 	snd_pcm_period_elapsed(substream);
100 
101 	jz4740_pcm_start_transfer(prtd, substream);
102 }
103 
jz4740_pcm_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)104 static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
105 	struct snd_pcm_hw_params *params)
106 {
107 	struct snd_pcm_runtime *runtime = substream->runtime;
108 	struct jz4740_runtime_data *prtd = runtime->private_data;
109 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
110 	struct jz4740_pcm_config *config;
111 
112 	config = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
113 
114 	if (!config)
115 		return 0;
116 
117 	if (!prtd->dma) {
118 		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
119 			prtd->dma = jz4740_dma_request(substream, "PCM Capture");
120 		else
121 			prtd->dma = jz4740_dma_request(substream, "PCM Playback");
122 	}
123 
124 	if (!prtd->dma)
125 		return -EBUSY;
126 
127 	jz4740_dma_configure(prtd->dma, &config->dma_config);
128 	prtd->fifo_addr = config->fifo_addr;
129 
130 	jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
131 
132 	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
133 	runtime->dma_bytes = params_buffer_bytes(params);
134 
135 	prtd->dma_period = params_period_bytes(params);
136 	prtd->dma_start = runtime->dma_addr;
137 	prtd->dma_pos = prtd->dma_start;
138 	prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
139 
140 	return 0;
141 }
142 
jz4740_pcm_hw_free(struct snd_pcm_substream * substream)143 static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
144 {
145 	struct jz4740_runtime_data *prtd = substream->runtime->private_data;
146 
147 	snd_pcm_set_runtime_buffer(substream, NULL);
148 	if (prtd->dma) {
149 		jz4740_dma_free(prtd->dma);
150 		prtd->dma = NULL;
151 	}
152 
153 	return 0;
154 }
155 
jz4740_pcm_prepare(struct snd_pcm_substream * substream)156 static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
157 {
158 	struct jz4740_runtime_data *prtd = substream->runtime->private_data;
159 
160 	if (!prtd->dma)
161 		return -EBUSY;
162 
163 	prtd->dma_pos = prtd->dma_start;
164 
165 	return 0;
166 }
167 
jz4740_pcm_trigger(struct snd_pcm_substream * substream,int cmd)168 static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
169 {
170 	struct snd_pcm_runtime *runtime = substream->runtime;
171 	struct jz4740_runtime_data *prtd = runtime->private_data;
172 
173 	switch (cmd) {
174 	case SNDRV_PCM_TRIGGER_START:
175 	case SNDRV_PCM_TRIGGER_RESUME:
176 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
177 		jz4740_pcm_start_transfer(prtd, substream);
178 		break;
179 	case SNDRV_PCM_TRIGGER_STOP:
180 	case SNDRV_PCM_TRIGGER_SUSPEND:
181 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
182 		jz4740_dma_disable(prtd->dma);
183 		break;
184 	default:
185 		break;
186 	}
187 
188 	return 0;
189 }
190 
jz4740_pcm_pointer(struct snd_pcm_substream * substream)191 static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
192 {
193 	struct snd_pcm_runtime *runtime = substream->runtime;
194 	struct jz4740_runtime_data *prtd = runtime->private_data;
195 	unsigned long byte_offset;
196 	snd_pcm_uframes_t offset;
197 	struct jz4740_dma_chan *dma = prtd->dma;
198 
199 	/* prtd->dma_pos points to the end of the current transfer. So by
200 	 * subtracting prdt->dma_start we get the offset to the end of the
201 	 * current period in bytes. By subtracting the residue of the transfer
202 	 * we get the current offset in bytes. */
203 	byte_offset = prtd->dma_pos - prtd->dma_start;
204 	byte_offset -= jz4740_dma_get_residue(dma);
205 
206 	offset = bytes_to_frames(runtime, byte_offset);
207 	if (offset >= runtime->buffer_size)
208 		offset = 0;
209 
210 	return offset;
211 }
212 
jz4740_pcm_open(struct snd_pcm_substream * substream)213 static int jz4740_pcm_open(struct snd_pcm_substream *substream)
214 {
215 	struct snd_pcm_runtime *runtime = substream->runtime;
216 	struct jz4740_runtime_data *prtd;
217 
218 	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
219 	if (prtd == NULL)
220 		return -ENOMEM;
221 
222 	snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
223 
224 	runtime->private_data = prtd;
225 
226 	return 0;
227 }
228 
jz4740_pcm_close(struct snd_pcm_substream * substream)229 static int jz4740_pcm_close(struct snd_pcm_substream *substream)
230 {
231 	struct snd_pcm_runtime *runtime = substream->runtime;
232 	struct jz4740_runtime_data *prtd = runtime->private_data;
233 
234 	kfree(prtd);
235 
236 	return 0;
237 }
238 
jz4740_pcm_mmap(struct snd_pcm_substream * substream,struct vm_area_struct * vma)239 static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
240 	struct vm_area_struct *vma)
241 {
242 	return remap_pfn_range(vma, vma->vm_start,
243 			substream->dma_buffer.addr >> PAGE_SHIFT,
244 			vma->vm_end - vma->vm_start, vma->vm_page_prot);
245 }
246 
247 static struct snd_pcm_ops jz4740_pcm_ops = {
248 	.open		= jz4740_pcm_open,
249 	.close		= jz4740_pcm_close,
250 	.ioctl		= snd_pcm_lib_ioctl,
251 	.hw_params	= jz4740_pcm_hw_params,
252 	.hw_free	= jz4740_pcm_hw_free,
253 	.prepare	= jz4740_pcm_prepare,
254 	.trigger	= jz4740_pcm_trigger,
255 	.pointer	= jz4740_pcm_pointer,
256 	.mmap		= jz4740_pcm_mmap,
257 };
258 
jz4740_pcm_preallocate_dma_buffer(struct snd_pcm * pcm,int stream)259 static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
260 {
261 	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
262 	struct snd_dma_buffer *buf = &substream->dma_buffer;
263 	size_t size = jz4740_pcm_hardware.buffer_bytes_max;
264 
265 	buf->dev.type = SNDRV_DMA_TYPE_DEV;
266 	buf->dev.dev = pcm->card->dev;
267 	buf->private_data = NULL;
268 
269 	buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
270 					  &buf->addr, GFP_KERNEL);
271 	if (!buf->area)
272 		return -ENOMEM;
273 
274 	buf->bytes = size;
275 
276 	return 0;
277 }
278 
jz4740_pcm_free(struct snd_pcm * pcm)279 static void jz4740_pcm_free(struct snd_pcm *pcm)
280 {
281 	struct snd_pcm_substream *substream;
282 	struct snd_dma_buffer *buf;
283 	int stream;
284 
285 	for (stream = 0; stream < SNDRV_PCM_STREAM_LAST; ++stream) {
286 		substream = pcm->streams[stream].substream;
287 		if (!substream)
288 			continue;
289 
290 		buf = &substream->dma_buffer;
291 		if (!buf->area)
292 			continue;
293 
294 		dma_free_noncoherent(pcm->card->dev, buf->bytes, buf->area,
295 				buf->addr);
296 		buf->area = NULL;
297 	}
298 }
299 
300 static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
301 
jz4740_pcm_new(struct snd_soc_pcm_runtime * rtd)302 static int jz4740_pcm_new(struct snd_soc_pcm_runtime *rtd)
303 {
304 	struct snd_card *card = rtd->card->snd_card;
305 	struct snd_pcm *pcm = rtd->pcm;
306 	int ret = 0;
307 
308 	if (!card->dev->dma_mask)
309 		card->dev->dma_mask = &jz4740_pcm_dmamask;
310 
311 	if (!card->dev->coherent_dma_mask)
312 		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
313 
314 	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
315 		ret = jz4740_pcm_preallocate_dma_buffer(pcm,
316 			SNDRV_PCM_STREAM_PLAYBACK);
317 		if (ret)
318 			goto err;
319 	}
320 
321 	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
322 		ret = jz4740_pcm_preallocate_dma_buffer(pcm,
323 			SNDRV_PCM_STREAM_CAPTURE);
324 		if (ret)
325 			goto err;
326 	}
327 
328 err:
329 	return ret;
330 }
331 
332 static struct snd_soc_platform_driver jz4740_soc_platform = {
333 		.ops		= &jz4740_pcm_ops,
334 		.pcm_new	= jz4740_pcm_new,
335 		.pcm_free	= jz4740_pcm_free,
336 };
337 
jz4740_pcm_probe(struct platform_device * pdev)338 static int __devinit jz4740_pcm_probe(struct platform_device *pdev)
339 {
340 	return snd_soc_register_platform(&pdev->dev, &jz4740_soc_platform);
341 }
342 
jz4740_pcm_remove(struct platform_device * pdev)343 static int __devexit jz4740_pcm_remove(struct platform_device *pdev)
344 {
345 	snd_soc_unregister_platform(&pdev->dev);
346 	return 0;
347 }
348 
349 static struct platform_driver jz4740_pcm_driver = {
350 	.probe = jz4740_pcm_probe,
351 	.remove = __devexit_p(jz4740_pcm_remove),
352 	.driver = {
353 		.name = "jz4740-pcm-audio",
354 		.owner = THIS_MODULE,
355 	},
356 };
357 
358 module_platform_driver(jz4740_pcm_driver);
359 
360 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
361 MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
362 MODULE_LICENSE("GPL");
363