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 if (WARN_ON(period_size
& 15))
95 s6dmac_put_fifo(DMA_MASK_DMAC(channel
), DMA_INDEX_CHNL(channel
),
96 src
, dst
, period_size
);
99 if (unlikely(prtd
->period
>= runtime
->periods
))
103 static irqreturn_t
s6000_pcm_irq(int irq
, void *data
)
105 struct snd_pcm
*pcm
= data
;
106 struct snd_soc_pcm_runtime
*runtime
= pcm
->private_data
;
107 struct s6000_runtime_data
*prtd
;
108 unsigned int has_xrun
;
109 int i
, ret
= IRQ_NONE
;
111 for (i
= 0; i
< 2; ++i
) {
112 struct snd_pcm_substream
*substream
= pcm
->streams
[i
].substream
;
113 struct s6000_pcm_dma_params
*params
=
114 snd_soc_dai_get_dma_data(runtime
->cpu_dai
, substream
);
116 unsigned int pending
;
118 if (substream
== SNDRV_PCM_STREAM_PLAYBACK
)
119 channel
= params
->dma_out
;
121 channel
= params
->dma_in
;
123 has_xrun
= params
->check_xrun(runtime
->cpu_dai
);
128 if (unlikely(has_xrun
& (1 << i
)) &&
129 substream
->runtime
&&
130 snd_pcm_running(substream
)) {
131 dev_dbg(pcm
->dev
, "xrun\n");
132 snd_pcm_stream_lock(substream
);
133 snd_pcm_stop(substream
, SNDRV_PCM_STATE_XRUN
);
134 snd_pcm_stream_unlock(substream
);
138 pending
= s6dmac_int_sources(DMA_MASK_DMAC(channel
),
139 DMA_INDEX_CHNL(channel
));
143 if (likely(substream
->runtime
&&
144 snd_pcm_running(substream
))) {
145 snd_pcm_period_elapsed(substream
);
146 dev_dbg(pcm
->dev
, "period elapsed %x %x\n",
147 s6dmac_cur_src(DMA_MASK_DMAC(channel
),
148 DMA_INDEX_CHNL(channel
)),
149 s6dmac_cur_dst(DMA_MASK_DMAC(channel
),
150 DMA_INDEX_CHNL(channel
)));
151 prtd
= substream
->runtime
->private_data
;
152 spin_lock(&prtd
->lock
);
153 s6000_pcm_enqueue_dma(substream
);
154 spin_unlock(&prtd
->lock
);
158 if (unlikely(pending
& ~7)) {
159 if (pending
& (1 << 3))
161 "s6000-pcm: DMA %x Underflow\n",
163 if (pending
& (1 << 4))
165 "s6000-pcm: DMA %x Overflow\n",
169 "s6000-pcm: DMA %x Master Error "
171 channel
, pending
>> 5);
179 static int s6000_pcm_start(struct snd_pcm_substream
*substream
)
181 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
182 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
183 struct s6000_pcm_dma_params
*par
;
188 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
190 spin_lock_irqsave(&prtd
->lock
, flags
);
192 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
199 s6dmac_enable_chan(DMA_MASK_DMAC(dma
), DMA_INDEX_CHNL(dma
),
200 1 /* priority 1 (0 is max) */,
201 0 /* peripheral requests w/o xfer length mode */,
202 srcinc
/* source address increment */,
203 srcinc
^1 /* destination address increment */,
204 0 /* chunksize 0 (skip impossible on this dma) */,
205 0 /* source skip after chunk (impossible) */,
206 0 /* destination skip after chunk (impossible) */,
207 4 /* 16 byte burst size */,
208 -1 /* don't conserve bandwidth */,
209 0 /* low watermark irq descriptor threshold */,
210 0 /* disable hardware timestamps */,
211 1 /* enable channel */);
213 s6000_pcm_enqueue_dma(substream
);
214 s6000_pcm_enqueue_dma(substream
);
216 spin_unlock_irqrestore(&prtd
->lock
, flags
);
221 static int s6000_pcm_stop(struct snd_pcm_substream
*substream
)
223 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
224 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
225 struct s6000_pcm_dma_params
*par
;
229 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
231 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
232 channel
= par
->dma_out
;
234 channel
= par
->dma_in
;
236 s6dmac_set_terminal_count(DMA_MASK_DMAC(channel
),
237 DMA_INDEX_CHNL(channel
), 0);
239 spin_lock_irqsave(&prtd
->lock
, flags
);
241 s6dmac_disable_chan(DMA_MASK_DMAC(channel
), DMA_INDEX_CHNL(channel
));
243 spin_unlock_irqrestore(&prtd
->lock
, flags
);
248 static int s6000_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
250 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
251 struct s6000_pcm_dma_params
*par
;
254 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
256 ret
= par
->trigger(substream
, cmd
, 0);
261 case SNDRV_PCM_TRIGGER_START
:
262 case SNDRV_PCM_TRIGGER_RESUME
:
263 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
264 ret
= s6000_pcm_start(substream
);
266 case SNDRV_PCM_TRIGGER_STOP
:
267 case SNDRV_PCM_TRIGGER_SUSPEND
:
268 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
269 ret
= s6000_pcm_stop(substream
);
277 return par
->trigger(substream
, cmd
, 1);
280 static int s6000_pcm_prepare(struct snd_pcm_substream
*substream
)
282 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
289 static snd_pcm_uframes_t
s6000_pcm_pointer(struct snd_pcm_substream
*substream
)
291 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
292 struct s6000_pcm_dma_params
*par
;
293 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
294 struct s6000_runtime_data
*prtd
= runtime
->private_data
;
299 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
301 spin_lock_irqsave(&prtd
->lock
, flags
);
303 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
304 count
= s6dmac_cur_src(DMA_MASK_DMAC(par
->dma_out
),
305 DMA_INDEX_CHNL(par
->dma_out
));
307 count
= s6dmac_cur_dst(DMA_MASK_DMAC(par
->dma_in
),
308 DMA_INDEX_CHNL(par
->dma_in
));
310 count
-= runtime
->dma_addr
;
312 spin_unlock_irqrestore(&prtd
->lock
, flags
);
314 offset
= bytes_to_frames(runtime
, count
);
315 if (unlikely(offset
>= runtime
->buffer_size
))
321 static int s6000_pcm_open(struct snd_pcm_substream
*substream
)
323 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
324 struct s6000_pcm_dma_params
*par
;
325 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
326 struct s6000_runtime_data
*prtd
;
329 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
330 snd_soc_set_runtime_hwparams(substream
, &s6000_pcm_hardware
);
332 ret
= snd_pcm_hw_constraint_step(runtime
, 0,
333 SNDRV_PCM_HW_PARAM_PERIOD_BYTES
, 16);
336 ret
= snd_pcm_hw_constraint_step(runtime
, 0,
337 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
, 16);
340 ret
= snd_pcm_hw_constraint_integer(runtime
,
341 SNDRV_PCM_HW_PARAM_PERIODS
);
345 if (par
->same_rate
) {
347 spin_lock(&par
->lock
); /* needed? */
349 spin_unlock(&par
->lock
);
351 ret
= snd_pcm_hw_constraint_minmax(runtime
,
352 SNDRV_PCM_HW_PARAM_RATE
,
359 prtd
= kzalloc(sizeof(struct s6000_runtime_data
), GFP_KERNEL
);
363 spin_lock_init(&prtd
->lock
);
365 runtime
->private_data
= prtd
;
370 static int s6000_pcm_close(struct snd_pcm_substream
*substream
)
372 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
373 struct s6000_runtime_data
*prtd
= runtime
->private_data
;
380 static int s6000_pcm_hw_params(struct snd_pcm_substream
*substream
,
381 struct snd_pcm_hw_params
*hw_params
)
383 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
384 struct s6000_pcm_dma_params
*par
;
386 ret
= snd_pcm_lib_malloc_pages(substream
,
387 params_buffer_bytes(hw_params
));
389 printk(KERN_WARNING
"s6000-pcm: allocation of memory failed\n");
393 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
395 if (par
->same_rate
) {
396 spin_lock(&par
->lock
);
397 if (par
->rate
== -1 ||
398 !(par
->in_use
& ~(1 << substream
->stream
))) {
399 par
->rate
= params_rate(hw_params
);
400 par
->in_use
|= 1 << substream
->stream
;
401 } else if (params_rate(hw_params
) != par
->rate
) {
402 snd_pcm_lib_free_pages(substream
);
403 par
->in_use
&= ~(1 << substream
->stream
);
406 spin_unlock(&par
->lock
);
411 static int s6000_pcm_hw_free(struct snd_pcm_substream
*substream
)
413 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
414 struct s6000_pcm_dma_params
*par
=
415 snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
417 spin_lock(&par
->lock
);
418 par
->in_use
&= ~(1 << substream
->stream
);
421 spin_unlock(&par
->lock
);
423 return snd_pcm_lib_free_pages(substream
);
426 static struct snd_pcm_ops s6000_pcm_ops
= {
427 .open
= s6000_pcm_open
,
428 .close
= s6000_pcm_close
,
429 .ioctl
= snd_pcm_lib_ioctl
,
430 .hw_params
= s6000_pcm_hw_params
,
431 .hw_free
= s6000_pcm_hw_free
,
432 .trigger
= s6000_pcm_trigger
,
433 .prepare
= s6000_pcm_prepare
,
434 .pointer
= s6000_pcm_pointer
,
437 static void s6000_pcm_free(struct snd_pcm
*pcm
)
439 struct snd_soc_pcm_runtime
*runtime
= pcm
->private_data
;
440 struct s6000_pcm_dma_params
*params
=
441 snd_soc_dai_get_dma_data(runtime
->cpu_dai
,
442 pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
);
444 free_irq(params
->irq
, pcm
);
445 snd_pcm_lib_preallocate_free_for_all(pcm
);
448 static int s6000_pcm_new(struct snd_soc_pcm_runtime
*runtime
)
450 struct snd_card
*card
= runtime
->card
->snd_card
;
451 struct snd_pcm
*pcm
= runtime
->pcm
;
452 struct s6000_pcm_dma_params
*params
;
455 params
= snd_soc_dai_get_dma_data(runtime
->cpu_dai
,
456 pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
);
458 res
= dma_coerce_mask_and_coherent(card
->dev
, 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 s6000_soc_platform_probe(struct platform_device
*pdev
)
505 return snd_soc_register_platform(&pdev
->dev
, &s6000_soc_platform
);
508 static int 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
= s6000_soc_platform_remove
,
524 module_platform_driver(s6000_pcm_driver
);
526 MODULE_AUTHOR("Daniel Gloeckner");
527 MODULE_DESCRIPTION("Stretch s6000 family PCM DMA module");
528 MODULE_LICENSE("GPL");