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>
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),
43 .buffer_bytes_max = 0x7ffffff0,
44 .period_bytes_min = 16,
45 .period_bytes_max = 0xfffff0,
47 .periods_max = 1024, /* no limit */
51 struct s6000_runtime_data {
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;
63 unsigned int period_size;
64 unsigned int dma_offset;
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) {
75 channel = par->dma_out;
79 channel = par->dma_in;
82 if (!s6dmac_channel_enabled(DMA_MASK_DMAC(channel),
83 DMA_INDEX_CHNL(channel)))
86 if (s6dmac_fifo_full(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel))) {
87 printk(KERN_ERR "s6000-pcm: fifo full\n");
91 BUG_ON(period_size & 15);
92 s6dmac_put_fifo(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel),
93 src, dst, period_size);
96 if (unlikely(prtd->period >= runtime->periods))
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;
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;
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);
130 pending = s6dmac_int_sources(DMA_MASK_DMAC(channel[i]),
131 DMA_INDEX_CHNL(channel[i]));
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))
153 "s6000-pcm: DMA %x Underflow\n",
155 if (pending & (1 << 4))
157 "s6000-pcm: DMA %x Overflow\n",
161 "s6000-pcm: DMA %x Master Error "
163 channel[i], pending >> 5);
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;
180 spin_lock_irqsave(&prtd->lock, flags);
182 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
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 threshold */,
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);
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;
219 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
220 channel = par->dma_out;
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);
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;
242 ret = par->trigger(substream, cmd, 0);
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);
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);
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;
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;
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));
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))
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;
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);
319 ret = snd_pcm_hw_constraint_step(runtime, 0,
320 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
323 ret = snd_pcm_hw_constraint_integer(runtime,
324 SNDRV_PCM_HW_PARAM_PERIODS);
328 if (par->same_rate) {
330 spin_lock(&par->lock); /* needed? */
332 spin_unlock(&par->lock);
334 ret = snd_pcm_hw_constraint_minmax(runtime,
335 SNDRV_PCM_HW_PARAM_RATE,
342 prtd = kzalloc(sizeof(struct s6000_runtime_data), GFP_KERNEL);
346 spin_lock_init(&prtd->lock);
348 runtime->private_data = prtd;
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;
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;
369 ret = snd_pcm_lib_malloc_pages(substream,
370 params_buffer_bytes(hw_params));
372 printk(KERN_WARNING "s6000-pcm: allocation of memory failed\n");
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);
387 spin_unlock(&par->lock);
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);
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;
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);
457 printk(KERN_ERR "s6000-pcm couldn't get IRQ\n");
461 res = snd_pcm_lib_preallocate_pages_for_all(pcm,
464 S6_PCM_PREALLOCATE_SIZE,
465 S6_PCM_PREALLOCATE_MAX);
467 printk(KERN_WARNING "s6000-pcm: preallocation failed\n");
469 spin_lock_init(¶ms->lock);
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");