SPI: Add helper macro for spi_driver boilerplate
[pohmelfs.git] / sound / soc / jz4740 / jz4740-pcm.c
blobd1989cde9f14d0d442531066f96b2a6e9c4f3525
1 /*
2 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
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.
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.
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>
22 #include <linux/dma-mapping.h>
24 #include <sound/core.h>
25 #include <sound/pcm.h>
26 #include <sound/pcm_params.h>
27 #include <sound/soc.h>
29 #include <asm/mach-jz4740/dma.h>
30 #include "jz4740-pcm.h"
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;
38 struct jz4740_dma_chan *dma;
40 dma_addr_t fifo_addr;
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,
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,
62 static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd,
63 struct snd_pcm_substream *substream)
65 unsigned long count;
67 if (prtd->dma_pos == prtd->dma_end)
68 prtd->dma_pos = prtd->dma_start;
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;
75 jz4740_dma_disable(prtd->dma);
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);
85 jz4740_dma_set_transfer_count(prtd->dma, count);
87 prtd->dma_pos += count;
89 jz4740_dma_enable(prtd->dma);
92 static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
93 void *dev_id)
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;
99 snd_pcm_period_elapsed(substream);
101 jz4740_pcm_start_transfer(prtd, substream);
104 static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
105 struct snd_pcm_hw_params *params)
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;
112 config = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
114 if (!config)
115 return 0;
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");
124 if (!prtd->dma)
125 return -EBUSY;
127 jz4740_dma_configure(prtd->dma, &config->dma_config);
128 prtd->fifo_addr = config->fifo_addr;
130 jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
132 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
133 runtime->dma_bytes = params_buffer_bytes(params);
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;
140 return 0;
143 static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
145 struct jz4740_runtime_data *prtd = substream->runtime->private_data;
147 snd_pcm_set_runtime_buffer(substream, NULL);
148 if (prtd->dma) {
149 jz4740_dma_free(prtd->dma);
150 prtd->dma = NULL;
153 return 0;
156 static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
158 struct jz4740_runtime_data *prtd = substream->runtime->private_data;
160 if (!prtd->dma)
161 return -EBUSY;
163 prtd->dma_pos = prtd->dma_start;
165 return 0;
168 static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
170 struct snd_pcm_runtime *runtime = substream->runtime;
171 struct jz4740_runtime_data *prtd = runtime->private_data;
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;
188 return 0;
191 static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
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;
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);
206 offset = bytes_to_frames(runtime, byte_offset);
207 if (offset >= runtime->buffer_size)
208 offset = 0;
210 return offset;
213 static int jz4740_pcm_open(struct snd_pcm_substream *substream)
215 struct snd_pcm_runtime *runtime = substream->runtime;
216 struct jz4740_runtime_data *prtd;
218 prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
219 if (prtd == NULL)
220 return -ENOMEM;
222 snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
224 runtime->private_data = prtd;
226 return 0;
229 static int jz4740_pcm_close(struct snd_pcm_substream *substream)
231 struct snd_pcm_runtime *runtime = substream->runtime;
232 struct jz4740_runtime_data *prtd = runtime->private_data;
234 kfree(prtd);
236 return 0;
239 static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
240 struct vm_area_struct *vma)
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);
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,
259 static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
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;
265 buf->dev.type = SNDRV_DMA_TYPE_DEV;
266 buf->dev.dev = pcm->card->dev;
267 buf->private_data = NULL;
269 buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
270 &buf->addr, GFP_KERNEL);
271 if (!buf->area)
272 return -ENOMEM;
274 buf->bytes = size;
276 return 0;
279 static void jz4740_pcm_free(struct snd_pcm *pcm)
281 struct snd_pcm_substream *substream;
282 struct snd_dma_buffer *buf;
283 int stream;
285 for (stream = 0; stream < SNDRV_PCM_STREAM_LAST; ++stream) {
286 substream = pcm->streams[stream].substream;
287 if (!substream)
288 continue;
290 buf = &substream->dma_buffer;
291 if (!buf->area)
292 continue;
294 dma_free_noncoherent(pcm->card->dev, buf->bytes, buf->area,
295 buf->addr);
296 buf->area = NULL;
300 static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
302 static int jz4740_pcm_new(struct snd_soc_pcm_runtime *rtd)
304 struct snd_card *card = rtd->card->snd_card;
305 struct snd_soc_dai *dai = rtd->cpu_dai;
306 struct snd_pcm *pcm = rtd->pcm;
307 int ret = 0;
309 if (!card->dev->dma_mask)
310 card->dev->dma_mask = &jz4740_pcm_dmamask;
312 if (!card->dev->coherent_dma_mask)
313 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
315 if (dai->driver->playback.channels_min) {
316 ret = jz4740_pcm_preallocate_dma_buffer(pcm,
317 SNDRV_PCM_STREAM_PLAYBACK);
318 if (ret)
319 goto err;
322 if (dai->driver->capture.channels_min) {
323 ret = jz4740_pcm_preallocate_dma_buffer(pcm,
324 SNDRV_PCM_STREAM_CAPTURE);
325 if (ret)
326 goto err;
329 err:
330 return ret;
333 static struct snd_soc_platform_driver jz4740_soc_platform = {
334 .ops = &jz4740_pcm_ops,
335 .pcm_new = jz4740_pcm_new,
336 .pcm_free = jz4740_pcm_free,
339 static int __devinit jz4740_pcm_probe(struct platform_device *pdev)
341 return snd_soc_register_platform(&pdev->dev, &jz4740_soc_platform);
344 static int __devexit jz4740_pcm_remove(struct platform_device *pdev)
346 snd_soc_unregister_platform(&pdev->dev);
347 return 0;
350 static struct platform_driver jz4740_pcm_driver = {
351 .probe = jz4740_pcm_probe,
352 .remove = __devexit_p(jz4740_pcm_remove),
353 .driver = {
354 .name = "jz4740-pcm-audio",
355 .owner = THIS_MODULE,
359 static int __init jz4740_soc_platform_init(void)
361 return platform_driver_register(&jz4740_pcm_driver);
363 module_init(jz4740_soc_platform_init);
365 static void __exit jz4740_soc_platform_exit(void)
367 return platform_driver_unregister(&jz4740_pcm_driver);
369 module_exit(jz4740_soc_platform_exit);
371 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
372 MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
373 MODULE_LICENSE("GPL");