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
;
63 unsigned int period_size
;
64 unsigned int dma_offset
;
68 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
70 period_size
= snd_pcm_lib_period_bytes(substream
);
71 dma_offset
= prtd
->period
* period_size
;
72 dma_pos
= runtime
->dma_addr
+ dma_offset
;
74 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
77 channel
= par
->dma_out
;
81 channel
= par
->dma_in
;
84 if (!s6dmac_channel_enabled(DMA_MASK_DMAC(channel
),
85 DMA_INDEX_CHNL(channel
)))
88 if (s6dmac_fifo_full(DMA_MASK_DMAC(channel
), DMA_INDEX_CHNL(channel
))) {
89 printk(KERN_ERR
"s6000-pcm: fifo full\n");
93 BUG_ON(period_size
& 15);
94 s6dmac_put_fifo(DMA_MASK_DMAC(channel
), DMA_INDEX_CHNL(channel
),
95 src
, dst
, period_size
);
98 if (unlikely(prtd
->period
>= runtime
->periods
))
102 static irqreturn_t
s6000_pcm_irq(int irq
, void *data
)
104 struct snd_pcm
*pcm
= data
;
105 struct snd_soc_pcm_runtime
*runtime
= pcm
->private_data
;
106 struct s6000_runtime_data
*prtd
;
107 unsigned int has_xrun
;
108 int i
, ret
= IRQ_NONE
;
110 for (i
= 0; i
< 2; ++i
) {
111 struct snd_pcm_substream
*substream
= pcm
->streams
[i
].substream
;
112 struct s6000_pcm_dma_params
*params
=
113 snd_soc_dai_get_dma_data(runtime
->cpu_dai
, substream
);
115 unsigned int pending
;
117 if (substream
== SNDRV_PCM_STREAM_PLAYBACK
)
118 channel
= params
->dma_out
;
120 channel
= params
->dma_in
;
122 has_xrun
= params
->check_xrun(runtime
->cpu_dai
);
127 if (unlikely(has_xrun
& (1 << i
)) &&
128 substream
->runtime
&&
129 snd_pcm_running(substream
)) {
130 dev_dbg(pcm
->dev
, "xrun\n");
131 snd_pcm_stop(substream
, SNDRV_PCM_STATE_XRUN
);
135 pending
= s6dmac_int_sources(DMA_MASK_DMAC(channel
),
136 DMA_INDEX_CHNL(channel
));
140 if (likely(substream
->runtime
&&
141 snd_pcm_running(substream
))) {
142 snd_pcm_period_elapsed(substream
);
143 dev_dbg(pcm
->dev
, "period elapsed %x %x\n",
144 s6dmac_cur_src(DMA_MASK_DMAC(channel
),
145 DMA_INDEX_CHNL(channel
)),
146 s6dmac_cur_dst(DMA_MASK_DMAC(channel
),
147 DMA_INDEX_CHNL(channel
)));
148 prtd
= substream
->runtime
->private_data
;
149 spin_lock(&prtd
->lock
);
150 s6000_pcm_enqueue_dma(substream
);
151 spin_unlock(&prtd
->lock
);
155 if (unlikely(pending
& ~7)) {
156 if (pending
& (1 << 3))
158 "s6000-pcm: DMA %x Underflow\n",
160 if (pending
& (1 << 4))
162 "s6000-pcm: DMA %x Overflow\n",
166 "s6000-pcm: DMA %x Master Error "
168 channel
, pending
>> 5);
176 static int s6000_pcm_start(struct snd_pcm_substream
*substream
)
178 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
179 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
180 struct s6000_pcm_dma_params
*par
;
185 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
187 spin_lock_irqsave(&prtd
->lock
, flags
);
189 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
196 s6dmac_enable_chan(DMA_MASK_DMAC(dma
), DMA_INDEX_CHNL(dma
),
197 1 /* priority 1 (0 is max) */,
198 0 /* peripheral requests w/o xfer length mode */,
199 srcinc
/* source address increment */,
200 srcinc
^1 /* destination address increment */,
201 0 /* chunksize 0 (skip impossible on this dma) */,
202 0 /* source skip after chunk (impossible) */,
203 0 /* destination skip after chunk (impossible) */,
204 4 /* 16 byte burst size */,
205 -1 /* don't conserve bandwidth */,
206 0 /* low watermark irq descriptor threshold */,
207 0 /* disable hardware timestamps */,
208 1 /* enable channel */);
210 s6000_pcm_enqueue_dma(substream
);
211 s6000_pcm_enqueue_dma(substream
);
213 spin_unlock_irqrestore(&prtd
->lock
, flags
);
218 static int s6000_pcm_stop(struct snd_pcm_substream
*substream
)
220 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
221 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
222 struct s6000_pcm_dma_params
*par
;
226 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
228 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
229 channel
= par
->dma_out
;
231 channel
= par
->dma_in
;
233 s6dmac_set_terminal_count(DMA_MASK_DMAC(channel
),
234 DMA_INDEX_CHNL(channel
), 0);
236 spin_lock_irqsave(&prtd
->lock
, flags
);
238 s6dmac_disable_chan(DMA_MASK_DMAC(channel
), DMA_INDEX_CHNL(channel
));
240 spin_unlock_irqrestore(&prtd
->lock
, flags
);
245 static int s6000_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
247 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
248 struct s6000_pcm_dma_params
*par
;
251 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
253 ret
= par
->trigger(substream
, cmd
, 0);
258 case SNDRV_PCM_TRIGGER_START
:
259 case SNDRV_PCM_TRIGGER_RESUME
:
260 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
261 ret
= s6000_pcm_start(substream
);
263 case SNDRV_PCM_TRIGGER_STOP
:
264 case SNDRV_PCM_TRIGGER_SUSPEND
:
265 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
266 ret
= s6000_pcm_stop(substream
);
274 return par
->trigger(substream
, cmd
, 1);
277 static int s6000_pcm_prepare(struct snd_pcm_substream
*substream
)
279 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
286 static snd_pcm_uframes_t
s6000_pcm_pointer(struct snd_pcm_substream
*substream
)
288 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
289 struct s6000_pcm_dma_params
*par
;
290 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
291 struct s6000_runtime_data
*prtd
= runtime
->private_data
;
296 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
298 spin_lock_irqsave(&prtd
->lock
, flags
);
300 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
301 count
= s6dmac_cur_src(DMA_MASK_DMAC(par
->dma_out
),
302 DMA_INDEX_CHNL(par
->dma_out
));
304 count
= s6dmac_cur_dst(DMA_MASK_DMAC(par
->dma_in
),
305 DMA_INDEX_CHNL(par
->dma_in
));
307 count
-= runtime
->dma_addr
;
309 spin_unlock_irqrestore(&prtd
->lock
, flags
);
311 offset
= bytes_to_frames(runtime
, count
);
312 if (unlikely(offset
>= runtime
->buffer_size
))
318 static int s6000_pcm_open(struct snd_pcm_substream
*substream
)
320 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
321 struct s6000_pcm_dma_params
*par
;
322 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
323 struct s6000_runtime_data
*prtd
;
326 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
327 snd_soc_set_runtime_hwparams(substream
, &s6000_pcm_hardware
);
329 ret
= snd_pcm_hw_constraint_step(runtime
, 0,
330 SNDRV_PCM_HW_PARAM_PERIOD_BYTES
, 16);
333 ret
= snd_pcm_hw_constraint_step(runtime
, 0,
334 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
, 16);
337 ret
= snd_pcm_hw_constraint_integer(runtime
,
338 SNDRV_PCM_HW_PARAM_PERIODS
);
342 if (par
->same_rate
) {
344 spin_lock(&par
->lock
); /* needed? */
346 spin_unlock(&par
->lock
);
348 ret
= snd_pcm_hw_constraint_minmax(runtime
,
349 SNDRV_PCM_HW_PARAM_RATE
,
356 prtd
= kzalloc(sizeof(struct s6000_runtime_data
), GFP_KERNEL
);
360 spin_lock_init(&prtd
->lock
);
362 runtime
->private_data
= prtd
;
367 static int s6000_pcm_close(struct snd_pcm_substream
*substream
)
369 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
370 struct s6000_runtime_data
*prtd
= runtime
->private_data
;
377 static int s6000_pcm_hw_params(struct snd_pcm_substream
*substream
,
378 struct snd_pcm_hw_params
*hw_params
)
380 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
381 struct s6000_pcm_dma_params
*par
;
383 ret
= snd_pcm_lib_malloc_pages(substream
,
384 params_buffer_bytes(hw_params
));
386 printk(KERN_WARNING
"s6000-pcm: allocation of memory failed\n");
390 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
392 if (par
->same_rate
) {
393 spin_lock(&par
->lock
);
394 if (par
->rate
== -1 ||
395 !(par
->in_use
& ~(1 << substream
->stream
))) {
396 par
->rate
= params_rate(hw_params
);
397 par
->in_use
|= 1 << substream
->stream
;
398 } else if (params_rate(hw_params
) != par
->rate
) {
399 snd_pcm_lib_free_pages(substream
);
400 par
->in_use
&= ~(1 << substream
->stream
);
403 spin_unlock(&par
->lock
);
408 static int s6000_pcm_hw_free(struct snd_pcm_substream
*substream
)
410 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
411 struct s6000_pcm_dma_params
*par
=
412 snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
414 spin_lock(&par
->lock
);
415 par
->in_use
&= ~(1 << substream
->stream
);
418 spin_unlock(&par
->lock
);
420 return snd_pcm_lib_free_pages(substream
);
423 static struct snd_pcm_ops s6000_pcm_ops
= {
424 .open
= s6000_pcm_open
,
425 .close
= s6000_pcm_close
,
426 .ioctl
= snd_pcm_lib_ioctl
,
427 .hw_params
= s6000_pcm_hw_params
,
428 .hw_free
= s6000_pcm_hw_free
,
429 .trigger
= s6000_pcm_trigger
,
430 .prepare
= s6000_pcm_prepare
,
431 .pointer
= s6000_pcm_pointer
,
434 static void s6000_pcm_free(struct snd_pcm
*pcm
)
436 struct snd_soc_pcm_runtime
*runtime
= pcm
->private_data
;
437 struct s6000_pcm_dma_params
*params
=
438 snd_soc_dai_get_dma_data(runtime
->cpu_dai
, pcm
->streams
[0].substream
);
440 free_irq(params
->irq
, pcm
);
441 snd_pcm_lib_preallocate_free_for_all(pcm
);
444 static u64 s6000_pcm_dmamask
= DMA_BIT_MASK(32);
446 static int s6000_pcm_new(struct snd_soc_pcm_runtime
*runtime
)
448 struct snd_card
*card
= runtime
->card
->snd_card
;
449 struct snd_soc_dai
*dai
= runtime
->cpu_dai
;
450 struct snd_pcm
*pcm
= runtime
->pcm
;
451 struct s6000_pcm_dma_params
*params
;
454 params
= snd_soc_dai_get_dma_data(runtime
->cpu_dai
,
455 pcm
->streams
[0].substream
);
457 if (!card
->dev
->dma_mask
)
458 card
->dev
->dma_mask
= &s6000_pcm_dmamask
;
459 if (!card
->dev
->coherent_dma_mask
)
460 card
->dev
->coherent_dma_mask
= DMA_BIT_MASK(32);
462 if (params
->dma_in
) {
463 s6dmac_disable_chan(DMA_MASK_DMAC(params
->dma_in
),
464 DMA_INDEX_CHNL(params
->dma_in
));
465 s6dmac_int_sources(DMA_MASK_DMAC(params
->dma_in
),
466 DMA_INDEX_CHNL(params
->dma_in
));
469 if (params
->dma_out
) {
470 s6dmac_disable_chan(DMA_MASK_DMAC(params
->dma_out
),
471 DMA_INDEX_CHNL(params
->dma_out
));
472 s6dmac_int_sources(DMA_MASK_DMAC(params
->dma_out
),
473 DMA_INDEX_CHNL(params
->dma_out
));
476 res
= request_irq(params
->irq
, s6000_pcm_irq
, IRQF_SHARED
,
479 printk(KERN_ERR
"s6000-pcm couldn't get IRQ\n");
483 res
= snd_pcm_lib_preallocate_pages_for_all(pcm
,
486 S6_PCM_PREALLOCATE_SIZE
,
487 S6_PCM_PREALLOCATE_MAX
);
489 printk(KERN_WARNING
"s6000-pcm: preallocation failed\n");
491 spin_lock_init(¶ms
->lock
);
497 static struct snd_soc_platform_driver s6000_soc_platform
= {
498 .ops
= &s6000_pcm_ops
,
499 .pcm_new
= s6000_pcm_new
,
500 .pcm_free
= s6000_pcm_free
,
503 static int __devinit
s6000_soc_platform_probe(struct platform_device
*pdev
)
505 return snd_soc_register_platform(&pdev
->dev
, &s6000_soc_platform
);
508 static int __devexit
s6000_soc_platform_remove(struct platform_device
*pdev
)
510 snd_soc_unregister_platform(&pdev
->dev
);
514 static struct platform_driver s6000_pcm_driver
= {
516 .name
= "s6000-pcm-audio",
517 .owner
= THIS_MODULE
,
520 .probe
= s6000_soc_platform_probe
,
521 .remove
= __devexit_p(s6000_soc_platform_remove
),
524 static int __init
snd_s6000_pcm_init(void)
526 return platform_driver_register(&s6000_pcm_driver
);
528 module_init(snd_s6000_pcm_init
);
530 static void __exit
snd_s6000_pcm_exit(void)
532 platform_driver_unregister(&s6000_pcm_driver
);
534 module_exit(snd_s6000_pcm_exit
);
536 MODULE_AUTHOR("Daniel Gloeckner");
537 MODULE_DESCRIPTION("Stretch s6000 family PCM DMA module");
538 MODULE_LICENSE("GPL");