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 .buffer_bytes_max
= 0x7ffffff0,
37 .period_bytes_min
= 16,
38 .period_bytes_max
= 0xfffff0,
40 .periods_max
= 1024, /* no limit */
44 struct s6000_runtime_data
{
46 int period
; /* current DMA period */
49 static void s6000_pcm_enqueue_dma(struct snd_pcm_substream
*substream
)
51 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
52 struct s6000_runtime_data
*prtd
= runtime
->private_data
;
53 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
54 struct s6000_pcm_dma_params
*par
;
56 unsigned int period_size
;
57 unsigned int dma_offset
;
61 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
63 period_size
= snd_pcm_lib_period_bytes(substream
);
64 dma_offset
= prtd
->period
* period_size
;
65 dma_pos
= runtime
->dma_addr
+ dma_offset
;
67 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
70 channel
= par
->dma_out
;
74 channel
= par
->dma_in
;
77 if (!s6dmac_channel_enabled(DMA_MASK_DMAC(channel
),
78 DMA_INDEX_CHNL(channel
)))
81 if (s6dmac_fifo_full(DMA_MASK_DMAC(channel
), DMA_INDEX_CHNL(channel
))) {
82 printk(KERN_ERR
"s6000-pcm: fifo full\n");
86 if (WARN_ON(period_size
& 15))
88 s6dmac_put_fifo(DMA_MASK_DMAC(channel
), DMA_INDEX_CHNL(channel
),
89 src
, dst
, period_size
);
92 if (unlikely(prtd
->period
>= runtime
->periods
))
96 static irqreturn_t
s6000_pcm_irq(int irq
, void *data
)
98 struct snd_pcm
*pcm
= data
;
99 struct snd_soc_pcm_runtime
*runtime
= pcm
->private_data
;
100 struct s6000_runtime_data
*prtd
;
101 unsigned int has_xrun
;
102 int i
, ret
= IRQ_NONE
;
104 for (i
= 0; i
< 2; ++i
) {
105 struct snd_pcm_substream
*substream
= pcm
->streams
[i
].substream
;
106 struct s6000_pcm_dma_params
*params
=
107 snd_soc_dai_get_dma_data(runtime
->cpu_dai
, substream
);
109 unsigned int pending
;
111 if (substream
== SNDRV_PCM_STREAM_PLAYBACK
)
112 channel
= params
->dma_out
;
114 channel
= params
->dma_in
;
116 has_xrun
= params
->check_xrun(runtime
->cpu_dai
);
121 if (unlikely(has_xrun
& (1 << i
)) &&
122 substream
->runtime
&&
123 snd_pcm_running(substream
)) {
124 dev_dbg(pcm
->dev
, "xrun\n");
125 snd_pcm_stream_lock(substream
);
126 snd_pcm_stop(substream
, SNDRV_PCM_STATE_XRUN
);
127 snd_pcm_stream_unlock(substream
);
131 pending
= s6dmac_int_sources(DMA_MASK_DMAC(channel
),
132 DMA_INDEX_CHNL(channel
));
136 if (likely(substream
->runtime
&&
137 snd_pcm_running(substream
))) {
138 snd_pcm_period_elapsed(substream
);
139 dev_dbg(pcm
->dev
, "period elapsed %x %x\n",
140 s6dmac_cur_src(DMA_MASK_DMAC(channel
),
141 DMA_INDEX_CHNL(channel
)),
142 s6dmac_cur_dst(DMA_MASK_DMAC(channel
),
143 DMA_INDEX_CHNL(channel
)));
144 prtd
= substream
->runtime
->private_data
;
145 spin_lock(&prtd
->lock
);
146 s6000_pcm_enqueue_dma(substream
);
147 spin_unlock(&prtd
->lock
);
151 if (unlikely(pending
& ~7)) {
152 if (pending
& (1 << 3))
154 "s6000-pcm: DMA %x Underflow\n",
156 if (pending
& (1 << 4))
158 "s6000-pcm: DMA %x Overflow\n",
162 "s6000-pcm: DMA %x Master Error "
164 channel
, pending
>> 5);
172 static int s6000_pcm_start(struct snd_pcm_substream
*substream
)
174 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
175 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
176 struct s6000_pcm_dma_params
*par
;
181 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
183 spin_lock_irqsave(&prtd
->lock
, flags
);
185 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
192 s6dmac_enable_chan(DMA_MASK_DMAC(dma
), DMA_INDEX_CHNL(dma
),
193 1 /* priority 1 (0 is max) */,
194 0 /* peripheral requests w/o xfer length mode */,
195 srcinc
/* source address increment */,
196 srcinc
^1 /* destination address increment */,
197 0 /* chunksize 0 (skip impossible on this dma) */,
198 0 /* source skip after chunk (impossible) */,
199 0 /* destination skip after chunk (impossible) */,
200 4 /* 16 byte burst size */,
201 -1 /* don't conserve bandwidth */,
202 0 /* low watermark irq descriptor threshold */,
203 0 /* disable hardware timestamps */,
204 1 /* enable channel */);
206 s6000_pcm_enqueue_dma(substream
);
207 s6000_pcm_enqueue_dma(substream
);
209 spin_unlock_irqrestore(&prtd
->lock
, flags
);
214 static int s6000_pcm_stop(struct snd_pcm_substream
*substream
)
216 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
217 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
218 struct s6000_pcm_dma_params
*par
;
222 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
224 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
225 channel
= par
->dma_out
;
227 channel
= par
->dma_in
;
229 s6dmac_set_terminal_count(DMA_MASK_DMAC(channel
),
230 DMA_INDEX_CHNL(channel
), 0);
232 spin_lock_irqsave(&prtd
->lock
, flags
);
234 s6dmac_disable_chan(DMA_MASK_DMAC(channel
), DMA_INDEX_CHNL(channel
));
236 spin_unlock_irqrestore(&prtd
->lock
, flags
);
241 static int s6000_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
243 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
244 struct s6000_pcm_dma_params
*par
;
247 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
249 ret
= par
->trigger(substream
, cmd
, 0);
254 case SNDRV_PCM_TRIGGER_START
:
255 case SNDRV_PCM_TRIGGER_RESUME
:
256 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
257 ret
= s6000_pcm_start(substream
);
259 case SNDRV_PCM_TRIGGER_STOP
:
260 case SNDRV_PCM_TRIGGER_SUSPEND
:
261 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
262 ret
= s6000_pcm_stop(substream
);
270 return par
->trigger(substream
, cmd
, 1);
273 static int s6000_pcm_prepare(struct snd_pcm_substream
*substream
)
275 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
282 static snd_pcm_uframes_t
s6000_pcm_pointer(struct snd_pcm_substream
*substream
)
284 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
285 struct s6000_pcm_dma_params
*par
;
286 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
287 struct s6000_runtime_data
*prtd
= runtime
->private_data
;
292 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
294 spin_lock_irqsave(&prtd
->lock
, flags
);
296 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
297 count
= s6dmac_cur_src(DMA_MASK_DMAC(par
->dma_out
),
298 DMA_INDEX_CHNL(par
->dma_out
));
300 count
= s6dmac_cur_dst(DMA_MASK_DMAC(par
->dma_in
),
301 DMA_INDEX_CHNL(par
->dma_in
));
303 count
-= runtime
->dma_addr
;
305 spin_unlock_irqrestore(&prtd
->lock
, flags
);
307 offset
= bytes_to_frames(runtime
, count
);
308 if (unlikely(offset
>= runtime
->buffer_size
))
314 static int s6000_pcm_open(struct snd_pcm_substream
*substream
)
316 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
317 struct s6000_pcm_dma_params
*par
;
318 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
319 struct s6000_runtime_data
*prtd
;
322 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
323 snd_soc_set_runtime_hwparams(substream
, &s6000_pcm_hardware
);
325 ret
= snd_pcm_hw_constraint_step(runtime
, 0,
326 SNDRV_PCM_HW_PARAM_PERIOD_BYTES
, 16);
329 ret
= snd_pcm_hw_constraint_step(runtime
, 0,
330 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
, 16);
333 ret
= snd_pcm_hw_constraint_integer(runtime
,
334 SNDRV_PCM_HW_PARAM_PERIODS
);
338 if (par
->same_rate
) {
340 spin_lock(&par
->lock
); /* needed? */
342 spin_unlock(&par
->lock
);
344 ret
= snd_pcm_hw_constraint_minmax(runtime
,
345 SNDRV_PCM_HW_PARAM_RATE
,
352 prtd
= kzalloc(sizeof(struct s6000_runtime_data
), GFP_KERNEL
);
356 spin_lock_init(&prtd
->lock
);
358 runtime
->private_data
= prtd
;
363 static int s6000_pcm_close(struct snd_pcm_substream
*substream
)
365 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
366 struct s6000_runtime_data
*prtd
= runtime
->private_data
;
373 static int s6000_pcm_hw_params(struct snd_pcm_substream
*substream
,
374 struct snd_pcm_hw_params
*hw_params
)
376 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
377 struct s6000_pcm_dma_params
*par
;
379 ret
= snd_pcm_lib_malloc_pages(substream
,
380 params_buffer_bytes(hw_params
));
382 printk(KERN_WARNING
"s6000-pcm: allocation of memory failed\n");
386 par
= snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
388 if (par
->same_rate
) {
389 spin_lock(&par
->lock
);
390 if (par
->rate
== -1 ||
391 !(par
->in_use
& ~(1 << substream
->stream
))) {
392 par
->rate
= params_rate(hw_params
);
393 par
->in_use
|= 1 << substream
->stream
;
394 } else if (params_rate(hw_params
) != par
->rate
) {
395 snd_pcm_lib_free_pages(substream
);
396 par
->in_use
&= ~(1 << substream
->stream
);
399 spin_unlock(&par
->lock
);
404 static int s6000_pcm_hw_free(struct snd_pcm_substream
*substream
)
406 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
407 struct s6000_pcm_dma_params
*par
=
408 snd_soc_dai_get_dma_data(soc_runtime
->cpu_dai
, substream
);
410 spin_lock(&par
->lock
);
411 par
->in_use
&= ~(1 << substream
->stream
);
414 spin_unlock(&par
->lock
);
416 return snd_pcm_lib_free_pages(substream
);
419 static struct snd_pcm_ops s6000_pcm_ops
= {
420 .open
= s6000_pcm_open
,
421 .close
= s6000_pcm_close
,
422 .ioctl
= snd_pcm_lib_ioctl
,
423 .hw_params
= s6000_pcm_hw_params
,
424 .hw_free
= s6000_pcm_hw_free
,
425 .trigger
= s6000_pcm_trigger
,
426 .prepare
= s6000_pcm_prepare
,
427 .pointer
= s6000_pcm_pointer
,
430 static void s6000_pcm_free(struct snd_pcm
*pcm
)
432 struct snd_soc_pcm_runtime
*runtime
= pcm
->private_data
;
433 struct s6000_pcm_dma_params
*params
=
434 snd_soc_dai_get_dma_data(runtime
->cpu_dai
,
435 pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
);
437 free_irq(params
->irq
, pcm
);
438 snd_pcm_lib_preallocate_free_for_all(pcm
);
441 static int s6000_pcm_new(struct snd_soc_pcm_runtime
*runtime
)
443 struct snd_card
*card
= runtime
->card
->snd_card
;
444 struct snd_pcm
*pcm
= runtime
->pcm
;
445 struct s6000_pcm_dma_params
*params
;
448 params
= snd_soc_dai_get_dma_data(runtime
->cpu_dai
,
449 pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
);
451 res
= dma_coerce_mask_and_coherent(card
->dev
, DMA_BIT_MASK(32));
455 if (params
->dma_in
) {
456 s6dmac_disable_chan(DMA_MASK_DMAC(params
->dma_in
),
457 DMA_INDEX_CHNL(params
->dma_in
));
458 s6dmac_int_sources(DMA_MASK_DMAC(params
->dma_in
),
459 DMA_INDEX_CHNL(params
->dma_in
));
462 if (params
->dma_out
) {
463 s6dmac_disable_chan(DMA_MASK_DMAC(params
->dma_out
),
464 DMA_INDEX_CHNL(params
->dma_out
));
465 s6dmac_int_sources(DMA_MASK_DMAC(params
->dma_out
),
466 DMA_INDEX_CHNL(params
->dma_out
));
469 res
= request_irq(params
->irq
, s6000_pcm_irq
, IRQF_SHARED
,
472 printk(KERN_ERR
"s6000-pcm couldn't get IRQ\n");
476 res
= snd_pcm_lib_preallocate_pages_for_all(pcm
,
479 S6_PCM_PREALLOCATE_SIZE
,
480 S6_PCM_PREALLOCATE_MAX
);
482 printk(KERN_WARNING
"s6000-pcm: preallocation failed\n");
484 spin_lock_init(¶ms
->lock
);
490 static struct snd_soc_platform_driver s6000_soc_platform
= {
491 .ops
= &s6000_pcm_ops
,
492 .pcm_new
= s6000_pcm_new
,
493 .pcm_free
= s6000_pcm_free
,
496 static int s6000_soc_platform_probe(struct platform_device
*pdev
)
498 return snd_soc_register_platform(&pdev
->dev
, &s6000_soc_platform
);
501 static int s6000_soc_platform_remove(struct platform_device
*pdev
)
503 snd_soc_unregister_platform(&pdev
->dev
);
507 static struct platform_driver s6000_pcm_driver
= {
509 .name
= "s6000-pcm-audio",
510 .owner
= THIS_MODULE
,
513 .probe
= s6000_soc_platform_probe
,
514 .remove
= s6000_soc_platform_remove
,
517 module_platform_driver(s6000_pcm_driver
);
519 MODULE_AUTHOR("Daniel Gloeckner");
520 MODULE_DESCRIPTION("Stretch s6000 family PCM DMA module");
521 MODULE_LICENSE("GPL");