Linux 2.6.31.8
[linux/fpc-iii.git] / sound / soc / s6000 / s6000-pcm.c
blob83b8028e209db1510b696988363e85444d483197
1 /*
2 * ALSA PCM interface for the Stetch s6000 family
4 * Author: Daniel Gloeckner, <dg@emlix.com>
5 * Copyright: (C) 2009 emlix GmbH <info@emlix.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/platform_device.h>
15 #include <linux/slab.h>
16 #include <linux/dma-mapping.h>
17 #include <linux/interrupt.h>
19 #include <sound/core.h>
20 #include <sound/pcm.h>
21 #include <sound/pcm_params.h>
22 #include <sound/soc.h>
24 #include <asm/dma.h>
25 #include <variant/dmac.h>
27 #include "s6000-pcm.h"
29 #define S6_PCM_PREALLOCATE_SIZE (96 * 1024)
30 #define S6_PCM_PREALLOCATE_MAX (2048 * 1024)
32 static struct snd_pcm_hardware s6000_pcm_hardware = {
33 .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
34 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
35 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_JOINT_DUPLEX),
36 .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE),
37 .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \
38 SNDRV_PCM_RATE_8000_192000),
39 .rate_min = 0,
40 .rate_max = 1562500,
41 .channels_min = 2,
42 .channels_max = 8,
43 .buffer_bytes_max = 0x7ffffff0,
44 .period_bytes_min = 16,
45 .period_bytes_max = 0xfffff0,
46 .periods_min = 2,
47 .periods_max = 1024, /* no limit */
48 .fifo_size = 0,
51 struct s6000_runtime_data {
52 spinlock_t lock;
53 int period; /* current DMA period */
56 static void s6000_pcm_enqueue_dma(struct snd_pcm_substream *substream)
58 struct snd_pcm_runtime *runtime = substream->runtime;
59 struct s6000_runtime_data *prtd = runtime->private_data;
60 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
61 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
62 int channel;
63 unsigned int period_size;
64 unsigned int dma_offset;
65 dma_addr_t dma_pos;
66 dma_addr_t src, dst;
68 period_size = snd_pcm_lib_period_bytes(substream);
69 dma_offset = prtd->period * period_size;
70 dma_pos = runtime->dma_addr + dma_offset;
72 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
73 src = dma_pos;
74 dst = par->sif_out;
75 channel = par->dma_out;
76 } else {
77 src = par->sif_in;
78 dst = dma_pos;
79 channel = par->dma_in;
82 if (!s6dmac_channel_enabled(DMA_MASK_DMAC(channel),
83 DMA_INDEX_CHNL(channel)))
84 return;
86 if (s6dmac_fifo_full(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel))) {
87 printk(KERN_ERR "s6000-pcm: fifo full\n");
88 return;
91 BUG_ON(period_size & 15);
92 s6dmac_put_fifo(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel),
93 src, dst, period_size);
95 prtd->period++;
96 if (unlikely(prtd->period >= runtime->periods))
97 prtd->period = 0;
100 static irqreturn_t s6000_pcm_irq(int irq, void *data)
102 struct snd_pcm *pcm = data;
103 struct snd_soc_pcm_runtime *runtime = pcm->private_data;
104 struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
105 struct s6000_runtime_data *prtd;
106 unsigned int has_xrun;
107 int i, ret = IRQ_NONE;
108 u32 channel[2] = {
109 [SNDRV_PCM_STREAM_PLAYBACK] = params->dma_out,
110 [SNDRV_PCM_STREAM_CAPTURE] = params->dma_in
113 has_xrun = params->check_xrun(runtime->dai->cpu_dai);
115 for (i = 0; i < ARRAY_SIZE(channel); ++i) {
116 struct snd_pcm_substream *substream = pcm->streams[i].substream;
117 unsigned int pending;
119 if (!channel[i])
120 continue;
122 if (unlikely(has_xrun & (1 << i)) &&
123 substream->runtime &&
124 snd_pcm_running(substream)) {
125 dev_dbg(pcm->dev, "xrun\n");
126 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
127 ret = IRQ_HANDLED;
130 pending = s6dmac_int_sources(DMA_MASK_DMAC(channel[i]),
131 DMA_INDEX_CHNL(channel[i]));
133 if (pending & 1) {
134 ret = IRQ_HANDLED;
135 if (likely(substream->runtime &&
136 snd_pcm_running(substream))) {
137 snd_pcm_period_elapsed(substream);
138 dev_dbg(pcm->dev, "period elapsed %x %x\n",
139 s6dmac_cur_src(DMA_MASK_DMAC(channel[i]),
140 DMA_INDEX_CHNL(channel[i])),
141 s6dmac_cur_dst(DMA_MASK_DMAC(channel[i]),
142 DMA_INDEX_CHNL(channel[i])));
143 prtd = substream->runtime->private_data;
144 spin_lock(&prtd->lock);
145 s6000_pcm_enqueue_dma(substream);
146 spin_unlock(&prtd->lock);
150 if (unlikely(pending & ~7)) {
151 if (pending & (1 << 3))
152 printk(KERN_WARNING
153 "s6000-pcm: DMA %x Underflow\n",
154 channel[i]);
155 if (pending & (1 << 4))
156 printk(KERN_WARNING
157 "s6000-pcm: DMA %x Overflow\n",
158 channel[i]);
159 if (pending & 0x1e0)
160 printk(KERN_WARNING
161 "s6000-pcm: DMA %x Master Error "
162 "(mask %x)\n",
163 channel[i], pending >> 5);
168 return ret;
171 static int s6000_pcm_start(struct snd_pcm_substream *substream)
173 struct s6000_runtime_data *prtd = substream->runtime->private_data;
174 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
175 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
176 unsigned long flags;
177 int srcinc;
178 u32 dma;
180 spin_lock_irqsave(&prtd->lock, flags);
182 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
183 srcinc = 1;
184 dma = par->dma_out;
185 } else {
186 srcinc = 0;
187 dma = par->dma_in;
189 s6dmac_enable_chan(DMA_MASK_DMAC(dma), DMA_INDEX_CHNL(dma),
190 1 /* priority 1 (0 is max) */,
191 0 /* peripheral requests w/o xfer length mode */,
192 srcinc /* source address increment */,
193 srcinc^1 /* destination address increment */,
194 0 /* chunksize 0 (skip impossible on this dma) */,
195 0 /* source skip after chunk (impossible) */,
196 0 /* destination skip after chunk (impossible) */,
197 4 /* 16 byte burst size */,
198 -1 /* don't conserve bandwidth */,
199 0 /* low watermark irq descriptor theshold */,
200 0 /* disable hardware timestamps */,
201 1 /* enable channel */);
203 s6000_pcm_enqueue_dma(substream);
204 s6000_pcm_enqueue_dma(substream);
206 spin_unlock_irqrestore(&prtd->lock, flags);
208 return 0;
211 static int s6000_pcm_stop(struct snd_pcm_substream *substream)
213 struct s6000_runtime_data *prtd = substream->runtime->private_data;
214 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
215 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
216 unsigned long flags;
217 u32 channel;
219 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
220 channel = par->dma_out;
221 else
222 channel = par->dma_in;
224 s6dmac_set_terminal_count(DMA_MASK_DMAC(channel),
225 DMA_INDEX_CHNL(channel), 0);
227 spin_lock_irqsave(&prtd->lock, flags);
229 s6dmac_disable_chan(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel));
231 spin_unlock_irqrestore(&prtd->lock, flags);
233 return 0;
236 static int s6000_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
238 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
239 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
240 int ret;
242 ret = par->trigger(substream, cmd, 0);
243 if (ret < 0)
244 return ret;
246 switch (cmd) {
247 case SNDRV_PCM_TRIGGER_START:
248 case SNDRV_PCM_TRIGGER_RESUME:
249 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
250 ret = s6000_pcm_start(substream);
251 break;
252 case SNDRV_PCM_TRIGGER_STOP:
253 case SNDRV_PCM_TRIGGER_SUSPEND:
254 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
255 ret = s6000_pcm_stop(substream);
256 break;
257 default:
258 ret = -EINVAL;
260 if (ret < 0)
261 return ret;
263 return par->trigger(substream, cmd, 1);
266 static int s6000_pcm_prepare(struct snd_pcm_substream *substream)
268 struct s6000_runtime_data *prtd = substream->runtime->private_data;
270 prtd->period = 0;
272 return 0;
275 static snd_pcm_uframes_t s6000_pcm_pointer(struct snd_pcm_substream *substream)
277 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
278 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
279 struct snd_pcm_runtime *runtime = substream->runtime;
280 struct s6000_runtime_data *prtd = runtime->private_data;
281 unsigned long flags;
282 unsigned int offset;
283 dma_addr_t count;
285 spin_lock_irqsave(&prtd->lock, flags);
287 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
288 count = s6dmac_cur_src(DMA_MASK_DMAC(par->dma_out),
289 DMA_INDEX_CHNL(par->dma_out));
290 else
291 count = s6dmac_cur_dst(DMA_MASK_DMAC(par->dma_in),
292 DMA_INDEX_CHNL(par->dma_in));
294 count -= runtime->dma_addr;
296 spin_unlock_irqrestore(&prtd->lock, flags);
298 offset = bytes_to_frames(runtime, count);
299 if (unlikely(offset >= runtime->buffer_size))
300 offset = 0;
302 return offset;
305 static int s6000_pcm_open(struct snd_pcm_substream *substream)
307 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
308 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
309 struct snd_pcm_runtime *runtime = substream->runtime;
310 struct s6000_runtime_data *prtd;
311 int ret;
313 snd_soc_set_runtime_hwparams(substream, &s6000_pcm_hardware);
315 ret = snd_pcm_hw_constraint_step(runtime, 0,
316 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 16);
317 if (ret < 0)
318 return ret;
319 ret = snd_pcm_hw_constraint_step(runtime, 0,
320 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
321 if (ret < 0)
322 return ret;
323 ret = snd_pcm_hw_constraint_integer(runtime,
324 SNDRV_PCM_HW_PARAM_PERIODS);
325 if (ret < 0)
326 return ret;
328 if (par->same_rate) {
329 int rate;
330 spin_lock(&par->lock); /* needed? */
331 rate = par->rate;
332 spin_unlock(&par->lock);
333 if (rate != -1) {
334 ret = snd_pcm_hw_constraint_minmax(runtime,
335 SNDRV_PCM_HW_PARAM_RATE,
336 rate, rate);
337 if (ret < 0)
338 return ret;
342 prtd = kzalloc(sizeof(struct s6000_runtime_data), GFP_KERNEL);
343 if (prtd == NULL)
344 return -ENOMEM;
346 spin_lock_init(&prtd->lock);
348 runtime->private_data = prtd;
350 return 0;
353 static int s6000_pcm_close(struct snd_pcm_substream *substream)
355 struct snd_pcm_runtime *runtime = substream->runtime;
356 struct s6000_runtime_data *prtd = runtime->private_data;
358 kfree(prtd);
360 return 0;
363 static int s6000_pcm_hw_params(struct snd_pcm_substream *substream,
364 struct snd_pcm_hw_params *hw_params)
366 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
367 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
368 int ret;
369 ret = snd_pcm_lib_malloc_pages(substream,
370 params_buffer_bytes(hw_params));
371 if (ret < 0) {
372 printk(KERN_WARNING "s6000-pcm: allocation of memory failed\n");
373 return ret;
376 if (par->same_rate) {
377 spin_lock(&par->lock);
378 if (par->rate == -1 ||
379 !(par->in_use & ~(1 << substream->stream))) {
380 par->rate = params_rate(hw_params);
381 par->in_use |= 1 << substream->stream;
382 } else if (params_rate(hw_params) != par->rate) {
383 snd_pcm_lib_free_pages(substream);
384 par->in_use &= ~(1 << substream->stream);
385 ret = -EBUSY;
387 spin_unlock(&par->lock);
389 return ret;
392 static int s6000_pcm_hw_free(struct snd_pcm_substream *substream)
394 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
395 struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
397 spin_lock(&par->lock);
398 par->in_use &= ~(1 << substream->stream);
399 if (!par->in_use)
400 par->rate = -1;
401 spin_unlock(&par->lock);
403 return snd_pcm_lib_free_pages(substream);
406 static struct snd_pcm_ops s6000_pcm_ops = {
407 .open = s6000_pcm_open,
408 .close = s6000_pcm_close,
409 .ioctl = snd_pcm_lib_ioctl,
410 .hw_params = s6000_pcm_hw_params,
411 .hw_free = s6000_pcm_hw_free,
412 .trigger = s6000_pcm_trigger,
413 .prepare = s6000_pcm_prepare,
414 .pointer = s6000_pcm_pointer,
417 static void s6000_pcm_free(struct snd_pcm *pcm)
419 struct snd_soc_pcm_runtime *runtime = pcm->private_data;
420 struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
422 free_irq(params->irq, pcm);
423 snd_pcm_lib_preallocate_free_for_all(pcm);
426 static u64 s6000_pcm_dmamask = DMA_32BIT_MASK;
428 static int s6000_pcm_new(struct snd_card *card,
429 struct snd_soc_dai *dai, struct snd_pcm *pcm)
431 struct snd_soc_pcm_runtime *runtime = pcm->private_data;
432 struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
433 int res;
435 if (!card->dev->dma_mask)
436 card->dev->dma_mask = &s6000_pcm_dmamask;
437 if (!card->dev->coherent_dma_mask)
438 card->dev->coherent_dma_mask = DMA_32BIT_MASK;
440 if (params->dma_in) {
441 s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_in),
442 DMA_INDEX_CHNL(params->dma_in));
443 s6dmac_int_sources(DMA_MASK_DMAC(params->dma_in),
444 DMA_INDEX_CHNL(params->dma_in));
447 if (params->dma_out) {
448 s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_out),
449 DMA_INDEX_CHNL(params->dma_out));
450 s6dmac_int_sources(DMA_MASK_DMAC(params->dma_out),
451 DMA_INDEX_CHNL(params->dma_out));
454 res = request_irq(params->irq, s6000_pcm_irq, IRQF_SHARED,
455 s6000_soc_platform.name, pcm);
456 if (res) {
457 printk(KERN_ERR "s6000-pcm couldn't get IRQ\n");
458 return res;
461 res = snd_pcm_lib_preallocate_pages_for_all(pcm,
462 SNDRV_DMA_TYPE_DEV,
463 card->dev,
464 S6_PCM_PREALLOCATE_SIZE,
465 S6_PCM_PREALLOCATE_MAX);
466 if (res)
467 printk(KERN_WARNING "s6000-pcm: preallocation failed\n");
469 spin_lock_init(&params->lock);
470 params->in_use = 0;
471 params->rate = -1;
472 return 0;
475 struct snd_soc_platform s6000_soc_platform = {
476 .name = "s6000-audio",
477 .pcm_ops = &s6000_pcm_ops,
478 .pcm_new = s6000_pcm_new,
479 .pcm_free = s6000_pcm_free,
481 EXPORT_SYMBOL_GPL(s6000_soc_platform);
483 static int __init s6000_pcm_init(void)
485 return snd_soc_register_platform(&s6000_soc_platform);
487 module_init(s6000_pcm_init);
489 static void __exit s6000_pcm_exit(void)
491 snd_soc_unregister_platform(&s6000_soc_platform);
493 module_exit(s6000_pcm_exit);
495 MODULE_AUTHOR("Daniel Gloeckner");
496 MODULE_DESCRIPTION("Stretch s6000 family PCM DMA module");
497 MODULE_LICENSE("GPL");