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_stream_lock(substream
);
132 snd_pcm_stop(substream
, SNDRV_PCM_STATE_XRUN
);
133 snd_pcm_stream_unlock(substream
);
137 pending
= s6dmac_int_sources(DMA_MASK_DMAC(channel
),
138 DMA_INDEX_CHNL(channel
));
142 if (likely(substream
->runtime
&&
143 snd_pcm_running(substream
))) {
144 snd_pcm_period_elapsed(substream
);
145 dev_dbg(pcm
->dev
, "period elapsed %x %x\n",
146 s6dmac_cur_src(DMA_MASK_DMAC(channel
),
147 DMA_INDEX_CHNL(channel
)),
148 s6dmac_cur_dst(DMA_MASK_DMAC(channel
),
149 DMA_INDEX_CHNL(channel
)));
150 prtd
= substream
->runtime
->private_data
;
151 spin_lock(&prtd
->lock
);
152 s6000_pcm_enqueue_dma(substream
);
153 spin_unlock(&prtd
->lock
);
157 if (unlikely(pending
& ~7)) {
158 if (pending
& (1 << 3))
160 "s6000-pcm: DMA %x Underflow\n",
162 if (pending
& (1 << 4))
164 "s6000-pcm: DMA %x Overflow\n",
168 "s6000-pcm: DMA %x Master Error "
170 channel
, pending
>> 5);
178 static int s6000_pcm_start(struct snd_pcm_substream
*substream
)
180 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
181 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
182 struct s6000_pcm_dma_params
*par
;
187 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
189 spin_lock_irqsave(&prtd
->lock
, flags
);
191 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
198 s6dmac_enable_chan(DMA_MASK_DMAC(dma
), DMA_INDEX_CHNL(dma
),
199 1 /* priority 1 (0 is max) */,
200 0 /* peripheral requests w/o xfer length mode */,
201 srcinc
/* source address increment */,
202 srcinc
^1 /* destination address increment */,
203 0 /* chunksize 0 (skip impossible on this dma) */,
204 0 /* source skip after chunk (impossible) */,
205 0 /* destination skip after chunk (impossible) */,
206 4 /* 16 byte burst size */,
207 -1 /* don't conserve bandwidth */,
208 0 /* low watermark irq descriptor threshold */,
209 0 /* disable hardware timestamps */,
210 1 /* enable channel */);
212 s6000_pcm_enqueue_dma(substream
);
213 s6000_pcm_enqueue_dma(substream
);
215 spin_unlock_irqrestore(&prtd
->lock
, flags
);
220 static int s6000_pcm_stop(struct snd_pcm_substream
*substream
)
222 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
223 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
224 struct s6000_pcm_dma_params
*par
;
228 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
230 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
231 channel
= par
->dma_out
;
233 channel
= par
->dma_in
;
235 s6dmac_set_terminal_count(DMA_MASK_DMAC(channel
),
236 DMA_INDEX_CHNL(channel
), 0);
238 spin_lock_irqsave(&prtd
->lock
, flags
);
240 s6dmac_disable_chan(DMA_MASK_DMAC(channel
), DMA_INDEX_CHNL(channel
));
242 spin_unlock_irqrestore(&prtd
->lock
, flags
);
247 static int s6000_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
249 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
250 struct s6000_pcm_dma_params
*par
;
253 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
255 ret
= par
->trigger(substream
, cmd
, 0);
260 case SNDRV_PCM_TRIGGER_START
:
261 case SNDRV_PCM_TRIGGER_RESUME
:
262 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
263 ret
= s6000_pcm_start(substream
);
265 case SNDRV_PCM_TRIGGER_STOP
:
266 case SNDRV_PCM_TRIGGER_SUSPEND
:
267 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
268 ret
= s6000_pcm_stop(substream
);
276 return par
->trigger(substream
, cmd
, 1);
279 static int s6000_pcm_prepare(struct snd_pcm_substream
*substream
)
281 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
288 static snd_pcm_uframes_t
s6000_pcm_pointer(struct snd_pcm_substream
*substream
)
290 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
291 struct s6000_pcm_dma_params
*par
;
292 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
293 struct s6000_runtime_data
*prtd
= runtime
->private_data
;
298 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
300 spin_lock_irqsave(&prtd
->lock
, flags
);
302 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
303 count
= s6dmac_cur_src(DMA_MASK_DMAC(par
->dma_out
),
304 DMA_INDEX_CHNL(par
->dma_out
));
306 count
= s6dmac_cur_dst(DMA_MASK_DMAC(par
->dma_in
),
307 DMA_INDEX_CHNL(par
->dma_in
));
309 count
-= runtime
->dma_addr
;
311 spin_unlock_irqrestore(&prtd
->lock
, flags
);
313 offset
= bytes_to_frames(runtime
, count
);
314 if (unlikely(offset
>= runtime
->buffer_size
))
320 static int s6000_pcm_open(struct snd_pcm_substream
*substream
)
322 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
323 struct s6000_pcm_dma_params
*par
;
324 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
325 struct s6000_runtime_data
*prtd
;
328 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
329 snd_soc_set_runtime_hwparams(substream
, &s6000_pcm_hardware
);
331 ret
= snd_pcm_hw_constraint_step(runtime
, 0,
332 SNDRV_PCM_HW_PARAM_PERIOD_BYTES
, 16);
335 ret
= snd_pcm_hw_constraint_step(runtime
, 0,
336 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
, 16);
339 ret
= snd_pcm_hw_constraint_integer(runtime
,
340 SNDRV_PCM_HW_PARAM_PERIODS
);
344 if (par
->same_rate
) {
346 spin_lock(&par
->lock
); /* needed? */
348 spin_unlock(&par
->lock
);
350 ret
= snd_pcm_hw_constraint_minmax(runtime
,
351 SNDRV_PCM_HW_PARAM_RATE
,
358 prtd
= kzalloc(sizeof(struct s6000_runtime_data
), GFP_KERNEL
);
362 spin_lock_init(&prtd
->lock
);
364 runtime
->private_data
= prtd
;
369 static int s6000_pcm_close(struct snd_pcm_substream
*substream
)
371 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
372 struct s6000_runtime_data
*prtd
= runtime
->private_data
;
379 static int s6000_pcm_hw_params(struct snd_pcm_substream
*substream
,
380 struct snd_pcm_hw_params
*hw_params
)
382 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
383 struct s6000_pcm_dma_params
*par
;
385 ret
= snd_pcm_lib_malloc_pages(substream
,
386 params_buffer_bytes(hw_params
));
388 printk(KERN_WARNING
"s6000-pcm: allocation of memory failed\n");
392 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
394 if (par
->same_rate
) {
395 spin_lock(&par
->lock
);
396 if (par
->rate
== -1 ||
397 !(par
->in_use
& ~(1 << substream
->stream
))) {
398 par
->rate
= params_rate(hw_params
);
399 par
->in_use
|= 1 << substream
->stream
;
400 } else if (params_rate(hw_params
) != par
->rate
) {
401 snd_pcm_lib_free_pages(substream
);
402 par
->in_use
&= ~(1 << substream
->stream
);
405 spin_unlock(&par
->lock
);
410 static int s6000_pcm_hw_free(struct snd_pcm_substream
*substream
)
412 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
413 struct s6000_pcm_dma_params
*par
=
414 snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
416 spin_lock(&par
->lock
);
417 par
->in_use
&= ~(1 << substream
->stream
);
420 spin_unlock(&par
->lock
);
422 return snd_pcm_lib_free_pages(substream
);
425 static struct snd_pcm_ops s6000_pcm_ops
= {
426 .open
= s6000_pcm_open
,
427 .close
= s6000_pcm_close
,
428 .ioctl
= snd_pcm_lib_ioctl
,
429 .hw_params
= s6000_pcm_hw_params
,
430 .hw_free
= s6000_pcm_hw_free
,
431 .trigger
= s6000_pcm_trigger
,
432 .prepare
= s6000_pcm_prepare
,
433 .pointer
= s6000_pcm_pointer
,
436 static void s6000_pcm_free(struct snd_pcm
*pcm
)
438 struct snd_soc_pcm_runtime
*runtime
= pcm
->private_data
;
439 struct s6000_pcm_dma_params
*params
=
440 snd_soc_dai_get_dma_data(runtime
->cpu_dai
,
441 pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
);
443 free_irq(params
->irq
, pcm
);
444 snd_pcm_lib_preallocate_free_for_all(pcm
);
447 static u64 s6000_pcm_dmamask
= DMA_BIT_MASK(32);
449 static int s6000_pcm_new(struct snd_soc_pcm_runtime
*runtime
)
451 struct snd_card
*card
= runtime
->card
->snd_card
;
452 struct snd_pcm
*pcm
= runtime
->pcm
;
453 struct s6000_pcm_dma_params
*params
;
456 params
= snd_soc_dai_get_dma_data(runtime
->cpu_dai
,
457 pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
);
459 if (!card
->dev
->dma_mask
)
460 card
->dev
->dma_mask
= &s6000_pcm_dmamask
;
461 if (!card
->dev
->coherent_dma_mask
)
462 card
->dev
->coherent_dma_mask
= DMA_BIT_MASK(32);
464 if (params
->dma_in
) {
465 s6dmac_disable_chan(DMA_MASK_DMAC(params
->dma_in
),
466 DMA_INDEX_CHNL(params
->dma_in
));
467 s6dmac_int_sources(DMA_MASK_DMAC(params
->dma_in
),
468 DMA_INDEX_CHNL(params
->dma_in
));
471 if (params
->dma_out
) {
472 s6dmac_disable_chan(DMA_MASK_DMAC(params
->dma_out
),
473 DMA_INDEX_CHNL(params
->dma_out
));
474 s6dmac_int_sources(DMA_MASK_DMAC(params
->dma_out
),
475 DMA_INDEX_CHNL(params
->dma_out
));
478 res
= request_irq(params
->irq
, s6000_pcm_irq
, IRQF_SHARED
,
481 printk(KERN_ERR
"s6000-pcm couldn't get IRQ\n");
485 res
= snd_pcm_lib_preallocate_pages_for_all(pcm
,
488 S6_PCM_PREALLOCATE_SIZE
,
489 S6_PCM_PREALLOCATE_MAX
);
491 printk(KERN_WARNING
"s6000-pcm: preallocation failed\n");
493 spin_lock_init(¶ms
->lock
);
499 static struct snd_soc_platform_driver s6000_soc_platform
= {
500 .ops
= &s6000_pcm_ops
,
501 .pcm_new
= s6000_pcm_new
,
502 .pcm_free
= s6000_pcm_free
,
505 static int s6000_soc_platform_probe(struct platform_device
*pdev
)
507 return snd_soc_register_platform(&pdev
->dev
, &s6000_soc_platform
);
510 static int s6000_soc_platform_remove(struct platform_device
*pdev
)
512 snd_soc_unregister_platform(&pdev
->dev
);
516 static struct platform_driver s6000_pcm_driver
= {
518 .name
= "s6000-pcm-audio",
519 .owner
= THIS_MODULE
,
522 .probe
= s6000_soc_platform_probe
,
523 .remove
= s6000_soc_platform_remove
,
526 module_platform_driver(s6000_pcm_driver
);
528 MODULE_AUTHOR("Daniel Gloeckner");
529 MODULE_DESCRIPTION("Stretch s6000 family PCM DMA module");
530 MODULE_LICENSE("GPL");