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
->dai
->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_pcm_dma_params
*params
=
107 snd_soc_dai_get_dma_data(soc_runtime
->dai
->cpu_dai
, substream
);
108 struct s6000_runtime_data
*prtd
;
109 unsigned int has_xrun
;
110 int i
, ret
= IRQ_NONE
;
112 [SNDRV_PCM_STREAM_PLAYBACK
] = params
->dma_out
,
113 [SNDRV_PCM_STREAM_CAPTURE
] = params
->dma_in
116 has_xrun
= params
->check_xrun(runtime
->dai
->cpu_dai
);
118 for (i
= 0; i
< ARRAY_SIZE(channel
); ++i
) {
119 struct snd_pcm_substream
*substream
= pcm
->streams
[i
].substream
;
120 unsigned int pending
;
125 if (unlikely(has_xrun
& (1 << i
)) &&
126 substream
->runtime
&&
127 snd_pcm_running(substream
)) {
128 dev_dbg(pcm
->dev
, "xrun\n");
129 snd_pcm_stop(substream
, SNDRV_PCM_STATE_XRUN
);
133 pending
= s6dmac_int_sources(DMA_MASK_DMAC(channel
[i
]),
134 DMA_INDEX_CHNL(channel
[i
]));
138 if (likely(substream
->runtime
&&
139 snd_pcm_running(substream
))) {
140 snd_pcm_period_elapsed(substream
);
141 dev_dbg(pcm
->dev
, "period elapsed %x %x\n",
142 s6dmac_cur_src(DMA_MASK_DMAC(channel
[i
]),
143 DMA_INDEX_CHNL(channel
[i
])),
144 s6dmac_cur_dst(DMA_MASK_DMAC(channel
[i
]),
145 DMA_INDEX_CHNL(channel
[i
])));
146 prtd
= substream
->runtime
->private_data
;
147 spin_lock(&prtd
->lock
);
148 s6000_pcm_enqueue_dma(substream
);
149 spin_unlock(&prtd
->lock
);
153 if (unlikely(pending
& ~7)) {
154 if (pending
& (1 << 3))
156 "s6000-pcm: DMA %x Underflow\n",
158 if (pending
& (1 << 4))
160 "s6000-pcm: DMA %x Overflow\n",
164 "s6000-pcm: DMA %x Master Error "
166 channel
[i
], pending
>> 5);
174 static int s6000_pcm_start(struct snd_pcm_substream
*substream
)
176 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
177 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
178 struct s6000_pcm_dma_params
*par
;
183 par
= snd_soc_dai_get_dma_data(soc_runtime
->dai
->cpu_dai
, substream
);
185 spin_lock_irqsave(&prtd
->lock
, flags
);
187 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
194 s6dmac_enable_chan(DMA_MASK_DMAC(dma
), DMA_INDEX_CHNL(dma
),
195 1 /* priority 1 (0 is max) */,
196 0 /* peripheral requests w/o xfer length mode */,
197 srcinc
/* source address increment */,
198 srcinc
^1 /* destination address increment */,
199 0 /* chunksize 0 (skip impossible on this dma) */,
200 0 /* source skip after chunk (impossible) */,
201 0 /* destination skip after chunk (impossible) */,
202 4 /* 16 byte burst size */,
203 -1 /* don't conserve bandwidth */,
204 0 /* low watermark irq descriptor threshold */,
205 0 /* disable hardware timestamps */,
206 1 /* enable channel */);
208 s6000_pcm_enqueue_dma(substream
);
209 s6000_pcm_enqueue_dma(substream
);
211 spin_unlock_irqrestore(&prtd
->lock
, flags
);
216 static int s6000_pcm_stop(struct snd_pcm_substream
*substream
)
218 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
219 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
220 struct s6000_pcm_dma_params
*par
;
224 par
= snd_soc_dai_get_dma_data(soc_runtime
->dai
->cpu_dai
, substream
);
226 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
227 channel
= par
->dma_out
;
229 channel
= par
->dma_in
;
231 s6dmac_set_terminal_count(DMA_MASK_DMAC(channel
),
232 DMA_INDEX_CHNL(channel
), 0);
234 spin_lock_irqsave(&prtd
->lock
, flags
);
236 s6dmac_disable_chan(DMA_MASK_DMAC(channel
), DMA_INDEX_CHNL(channel
));
238 spin_unlock_irqrestore(&prtd
->lock
, flags
);
243 static int s6000_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
245 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
246 struct s6000_pcm_dma_params
*par
;
249 par
= snd_soc_dai_get_dma_data(soc_runtime
->dai
->cpu_dai
, substream
);
251 ret
= par
->trigger(substream
, cmd
, 0);
256 case SNDRV_PCM_TRIGGER_START
:
257 case SNDRV_PCM_TRIGGER_RESUME
:
258 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
259 ret
= s6000_pcm_start(substream
);
261 case SNDRV_PCM_TRIGGER_STOP
:
262 case SNDRV_PCM_TRIGGER_SUSPEND
:
263 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
264 ret
= s6000_pcm_stop(substream
);
272 return par
->trigger(substream
, cmd
, 1);
275 static int s6000_pcm_prepare(struct snd_pcm_substream
*substream
)
277 struct s6000_runtime_data
*prtd
= substream
->runtime
->private_data
;
284 static snd_pcm_uframes_t
s6000_pcm_pointer(struct snd_pcm_substream
*substream
)
286 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
287 struct s6000_pcm_dma_params
*par
;
288 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
289 struct s6000_runtime_data
*prtd
= runtime
->private_data
;
294 par
= snd_soc_dai_get_dma_data(soc_runtime
->dai
->cpu_dai
, substream
);
296 spin_lock_irqsave(&prtd
->lock
, flags
);
298 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
299 count
= s6dmac_cur_src(DMA_MASK_DMAC(par
->dma_out
),
300 DMA_INDEX_CHNL(par
->dma_out
));
302 count
= s6dmac_cur_dst(DMA_MASK_DMAC(par
->dma_in
),
303 DMA_INDEX_CHNL(par
->dma_in
));
305 count
-= runtime
->dma_addr
;
307 spin_unlock_irqrestore(&prtd
->lock
, flags
);
309 offset
= bytes_to_frames(runtime
, count
);
310 if (unlikely(offset
>= runtime
->buffer_size
))
316 static int s6000_pcm_open(struct snd_pcm_substream
*substream
)
318 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
319 struct s6000_pcm_dma_params
*par
;
320 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
321 struct s6000_runtime_data
*prtd
;
324 par
= snd_soc_dai_get_dma_data(soc_runtime
->dai
->cpu_dai
, substream
);
325 snd_soc_set_runtime_hwparams(substream
, &s6000_pcm_hardware
);
327 ret
= snd_pcm_hw_constraint_step(runtime
, 0,
328 SNDRV_PCM_HW_PARAM_PERIOD_BYTES
, 16);
331 ret
= snd_pcm_hw_constraint_step(runtime
, 0,
332 SNDRV_PCM_HW_PARAM_BUFFER_BYTES
, 16);
335 ret
= snd_pcm_hw_constraint_integer(runtime
,
336 SNDRV_PCM_HW_PARAM_PERIODS
);
340 if (par
->same_rate
) {
342 spin_lock(&par
->lock
); /* needed? */
344 spin_unlock(&par
->lock
);
346 ret
= snd_pcm_hw_constraint_minmax(runtime
,
347 SNDRV_PCM_HW_PARAM_RATE
,
354 prtd
= kzalloc(sizeof(struct s6000_runtime_data
), GFP_KERNEL
);
358 spin_lock_init(&prtd
->lock
);
360 runtime
->private_data
= prtd
;
365 static int s6000_pcm_close(struct snd_pcm_substream
*substream
)
367 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
368 struct s6000_runtime_data
*prtd
= runtime
->private_data
;
375 static int s6000_pcm_hw_params(struct snd_pcm_substream
*substream
,
376 struct snd_pcm_hw_params
*hw_params
)
378 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
379 struct s6000_pcm_dma_params
*par
;
381 ret
= snd_pcm_lib_malloc_pages(substream
,
382 params_buffer_bytes(hw_params
));
384 printk(KERN_WARNING
"s6000-pcm: allocation of memory failed\n");
388 par
= snd_soc_dai_get_dma_data(soc_runtime
->dai
->cpu_dai
, substream
);
390 if (par
->same_rate
) {
391 spin_lock(&par
->lock
);
392 if (par
->rate
== -1 ||
393 !(par
->in_use
& ~(1 << substream
->stream
))) {
394 par
->rate
= params_rate(hw_params
);
395 par
->in_use
|= 1 << substream
->stream
;
396 } else if (params_rate(hw_params
) != par
->rate
) {
397 snd_pcm_lib_free_pages(substream
);
398 par
->in_use
&= ~(1 << substream
->stream
);
401 spin_unlock(&par
->lock
);
406 static int s6000_pcm_hw_free(struct snd_pcm_substream
*substream
)
408 struct snd_soc_pcm_runtime
*soc_runtime
= substream
->private_data
;
409 struct s6000_pcm_dma_params
*par
=
410 snd_soc_dai_get_dma_data(soc_runtime
->dai
->cpu_dai
, substream
);
412 spin_lock(&par
->lock
);
413 par
->in_use
&= ~(1 << substream
->stream
);
416 spin_unlock(&par
->lock
);
418 return snd_pcm_lib_free_pages(substream
);
421 static struct snd_pcm_ops s6000_pcm_ops
= {
422 .open
= s6000_pcm_open
,
423 .close
= s6000_pcm_close
,
424 .ioctl
= snd_pcm_lib_ioctl
,
425 .hw_params
= s6000_pcm_hw_params
,
426 .hw_free
= s6000_pcm_hw_free
,
427 .trigger
= s6000_pcm_trigger
,
428 .prepare
= s6000_pcm_prepare
,
429 .pointer
= s6000_pcm_pointer
,
432 static void s6000_pcm_free(struct snd_pcm
*pcm
)
434 struct snd_soc_pcm_runtime
*runtime
= pcm
->private_data
;
435 struct s6000_pcm_dma_params
*params
=
436 snd_soc_dai_get_dma_data(soc_runtime
->dai
->cpu_dai
, substream
);
438 free_irq(params
->irq
, pcm
);
439 snd_pcm_lib_preallocate_free_for_all(pcm
);
442 static u64 s6000_pcm_dmamask
= DMA_BIT_MASK(32);
444 static int s6000_pcm_new(struct snd_card
*card
,
445 struct snd_soc_dai
*dai
, struct snd_pcm
*pcm
)
447 struct snd_soc_pcm_runtime
*runtime
= pcm
->private_data
;
448 struct s6000_pcm_dma_params
*params
;
451 params
= snd_soc_dai_get_dma_data(soc_runtime
->dai
->cpu_dai
, substream
);
453 if (!card
->dev
->dma_mask
)
454 card
->dev
->dma_mask
= &s6000_pcm_dmamask
;
455 if (!card
->dev
->coherent_dma_mask
)
456 card
->dev
->coherent_dma_mask
= DMA_BIT_MASK(32);
458 if (params
->dma_in
) {
459 s6dmac_disable_chan(DMA_MASK_DMAC(params
->dma_in
),
460 DMA_INDEX_CHNL(params
->dma_in
));
461 s6dmac_int_sources(DMA_MASK_DMAC(params
->dma_in
),
462 DMA_INDEX_CHNL(params
->dma_in
));
465 if (params
->dma_out
) {
466 s6dmac_disable_chan(DMA_MASK_DMAC(params
->dma_out
),
467 DMA_INDEX_CHNL(params
->dma_out
));
468 s6dmac_int_sources(DMA_MASK_DMAC(params
->dma_out
),
469 DMA_INDEX_CHNL(params
->dma_out
));
472 res
= request_irq(params
->irq
, s6000_pcm_irq
, IRQF_SHARED
,
473 s6000_soc_platform
.name
, pcm
);
475 printk(KERN_ERR
"s6000-pcm couldn't get IRQ\n");
479 res
= snd_pcm_lib_preallocate_pages_for_all(pcm
,
482 S6_PCM_PREALLOCATE_SIZE
,
483 S6_PCM_PREALLOCATE_MAX
);
485 printk(KERN_WARNING
"s6000-pcm: preallocation failed\n");
487 spin_lock_init(¶ms
->lock
);
493 struct snd_soc_platform s6000_soc_platform
= {
494 .name
= "s6000-audio",
495 .pcm_ops
= &s6000_pcm_ops
,
496 .pcm_new
= s6000_pcm_new
,
497 .pcm_free
= s6000_pcm_free
,
499 EXPORT_SYMBOL_GPL(s6000_soc_platform
);
501 static int __init
s6000_pcm_init(void)
503 return snd_soc_register_platform(&s6000_soc_platform
);
505 module_init(s6000_pcm_init
);
507 static void __exit
s6000_pcm_exit(void)
509 snd_soc_unregister_platform(&s6000_soc_platform
);
511 module_exit(s6000_pcm_exit
);
513 MODULE_AUTHOR("Daniel Gloeckner");
514 MODULE_DESCRIPTION("Stretch s6000 family PCM DMA module");
515 MODULE_LICENSE("GPL");