2
* dma.c -- ALSA Soc Audio Layer
4
* (c) 2006 Wolfson Microelectronics PLC.
5
* Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
7
* Copyright 2004-2005 Simtec Electronics
8
* http://armlinux.simtec.co.uk/
9
* Ben Dooks <ben@simtec.co.uk>
11
* This program is free software; you can redistribute it and/or modify it
12
* under the terms of the GNU General Public License as published by the
13
* Free Software Foundation; either version 2 of the License, or (at your
14
* option) any later version.
17
#include <linux/slab.h>
18
#include <linux/dma-mapping.h>
20
#include <sound/soc.h>
21
#include <sound/pcm_params.h>
24
#include <mach/hardware.h>
29
#define ST_RUNNING (1<<0)
30
#define ST_OPENED (1<<1)
32
static const struct snd_pcm_hardware dma_hardware = {
33
.info = SNDRV_PCM_INFO_INTERLEAVED |
34
SNDRV_PCM_INFO_BLOCK_TRANSFER |
36
SNDRV_PCM_INFO_MMAP_VALID |
37
SNDRV_PCM_INFO_PAUSE |
38
SNDRV_PCM_INFO_RESUME,
39
.formats = SNDRV_PCM_FMTBIT_S16_LE |
40
SNDRV_PCM_FMTBIT_U16_LE |
45
.buffer_bytes_max = 128*1024,
46
.period_bytes_min = PAGE_SIZE,
47
.period_bytes_max = PAGE_SIZE*2,
56
unsigned int dma_loaded;
57
unsigned int dma_limit;
58
unsigned int dma_period;
62
struct s3c_dma_params *params;
67
* place a dma buffer onto the queue for the dma system
70
static void dma_enqueue(struct snd_pcm_substream *substream)
72
struct runtime_data *prtd = substream->runtime->private_data;
73
dma_addr_t pos = prtd->dma_pos;
77
pr_debug("Entered %s\n", __func__);
79
if (s3c_dma_has_circular())
80
limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
82
limit = prtd->dma_limit;
84
pr_debug("%s: loaded %d, limit %d\n",
85
__func__, prtd->dma_loaded, limit);
87
while (prtd->dma_loaded < limit) {
88
unsigned long len = prtd->dma_period;
90
pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
92
if ((pos + len) > prtd->dma_end) {
93
len = prtd->dma_end - pos;
94
pr_debug("%s: corrected dma len %ld\n", __func__, len);
97
ret = s3c2410_dma_enqueue(prtd->params->channel,
102
pos += prtd->dma_period;
103
if (pos >= prtd->dma_end)
104
pos = prtd->dma_start;
112
static void audio_buffdone(struct s3c2410_dma_chan *channel,
113
void *dev_id, int size,
114
enum s3c2410_dma_buffresult result)
116
struct snd_pcm_substream *substream = dev_id;
117
struct runtime_data *prtd;
119
pr_debug("Entered %s\n", __func__);
121
if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR)
124
prtd = substream->runtime->private_data;
127
snd_pcm_period_elapsed(substream);
129
spin_lock(&prtd->lock);
130
if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) {
132
dma_enqueue(substream);
135
spin_unlock(&prtd->lock);
138
static int dma_hw_params(struct snd_pcm_substream *substream,
139
struct snd_pcm_hw_params *params)
141
struct snd_pcm_runtime *runtime = substream->runtime;
142
struct runtime_data *prtd = runtime->private_data;
143
struct snd_soc_pcm_runtime *rtd = substream->private_data;
144
unsigned long totbytes = params_buffer_bytes(params);
145
struct s3c_dma_params *dma =
146
snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
150
pr_debug("Entered %s\n", __func__);
152
/* return if this is a bufferless transfer e.g.
153
* codec <--> BT codec or GSM modem -- lg FIXME */
157
/* this may get called several times by oss emulation
158
* with different params -HW */
159
if (prtd->params == NULL) {
163
pr_debug("params %p, client %p, channel %d\n", prtd->params,
164
prtd->params->client, prtd->params->channel);
166
ret = s3c2410_dma_request(prtd->params->channel,
167
prtd->params->client, NULL);
170
printk(KERN_ERR "failed to get dma channel\n");
174
/* use the circular buffering if we have it available. */
175
if (s3c_dma_has_circular())
176
s3c2410_dma_setflags(prtd->params->channel,
177
S3C2410_DMAF_CIRCULAR);
180
s3c2410_dma_set_buffdone_fn(prtd->params->channel,
183
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
185
runtime->dma_bytes = totbytes;
187
spin_lock_irq(&prtd->lock);
188
prtd->dma_loaded = 0;
189
prtd->dma_limit = runtime->hw.periods_min;
190
prtd->dma_period = params_period_bytes(params);
191
prtd->dma_start = runtime->dma_addr;
192
prtd->dma_pos = prtd->dma_start;
193
prtd->dma_end = prtd->dma_start + totbytes;
194
spin_unlock_irq(&prtd->lock);
199
static int dma_hw_free(struct snd_pcm_substream *substream)
201
struct runtime_data *prtd = substream->runtime->private_data;
203
pr_debug("Entered %s\n", __func__);
205
/* TODO - do we need to ensure DMA flushed */
206
snd_pcm_set_runtime_buffer(substream, NULL);
209
s3c2410_dma_free(prtd->params->channel, prtd->params->client);
216
static int dma_prepare(struct snd_pcm_substream *substream)
218
struct runtime_data *prtd = substream->runtime->private_data;
221
pr_debug("Entered %s\n", __func__);
223
/* return if this is a bufferless transfer e.g.
224
* codec <--> BT codec or GSM modem -- lg FIXME */
228
/* channel needs configuring for mem=>device, increment memory addr,
229
* sync to pclk, half-word transfers to the IIS-FIFO. */
230
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
231
s3c2410_dma_devconfig(prtd->params->channel,
233
prtd->params->dma_addr);
235
s3c2410_dma_devconfig(prtd->params->channel,
237
prtd->params->dma_addr);
240
s3c2410_dma_config(prtd->params->channel,
241
prtd->params->dma_size);
243
/* flush the DMA channel */
244
s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH);
245
prtd->dma_loaded = 0;
246
prtd->dma_pos = prtd->dma_start;
248
/* enqueue dma buffers */
249
dma_enqueue(substream);
254
static int dma_trigger(struct snd_pcm_substream *substream, int cmd)
256
struct runtime_data *prtd = substream->runtime->private_data;
259
pr_debug("Entered %s\n", __func__);
261
spin_lock(&prtd->lock);
264
case SNDRV_PCM_TRIGGER_START:
265
case SNDRV_PCM_TRIGGER_RESUME:
266
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
267
prtd->state |= ST_RUNNING;
268
s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START);
271
case SNDRV_PCM_TRIGGER_STOP:
272
case SNDRV_PCM_TRIGGER_SUSPEND:
273
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
274
prtd->state &= ~ST_RUNNING;
275
s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STOP);
283
spin_unlock(&prtd->lock);
288
static snd_pcm_uframes_t
289
dma_pointer(struct snd_pcm_substream *substream)
291
struct snd_pcm_runtime *runtime = substream->runtime;
292
struct runtime_data *prtd = runtime->private_data;
296
pr_debug("Entered %s\n", __func__);
298
spin_lock(&prtd->lock);
299
s3c2410_dma_getposition(prtd->params->channel, &src, &dst);
301
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
302
res = dst - prtd->dma_start;
304
res = src - prtd->dma_start;
306
spin_unlock(&prtd->lock);
308
pr_debug("Pointer %x %x\n", src, dst);
310
/* we seem to be getting the odd error from the pcm library due
311
* to out-of-bounds pointers. this is maybe due to the dma engine
312
* not having loaded the new values for the channel before being
313
* callled... (todo - fix )
316
if (res >= snd_pcm_lib_buffer_bytes(substream)) {
317
if (res == snd_pcm_lib_buffer_bytes(substream))
321
return bytes_to_frames(substream->runtime, res);
324
static int dma_open(struct snd_pcm_substream *substream)
326
struct snd_pcm_runtime *runtime = substream->runtime;
327
struct runtime_data *prtd;
329
pr_debug("Entered %s\n", __func__);
331
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
332
snd_soc_set_runtime_hwparams(substream, &dma_hardware);
334
prtd = kzalloc(sizeof(struct runtime_data), GFP_KERNEL);
338
spin_lock_init(&prtd->lock);
340
runtime->private_data = prtd;
344
static int dma_close(struct snd_pcm_substream *substream)
346
struct snd_pcm_runtime *runtime = substream->runtime;
347
struct runtime_data *prtd = runtime->private_data;
349
pr_debug("Entered %s\n", __func__);
352
pr_debug("dma_close called with prtd == NULL\n");
359
static int dma_mmap(struct snd_pcm_substream *substream,
360
struct vm_area_struct *vma)
362
struct snd_pcm_runtime *runtime = substream->runtime;
364
pr_debug("Entered %s\n", __func__);
366
return dma_mmap_writecombine(substream->pcm->card->dev, vma,
372
static struct snd_pcm_ops dma_ops = {
375
.ioctl = snd_pcm_lib_ioctl,
376
.hw_params = dma_hw_params,
377
.hw_free = dma_hw_free,
378
.prepare = dma_prepare,
379
.trigger = dma_trigger,
380
.pointer = dma_pointer,
384
static int preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
386
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
387
struct snd_dma_buffer *buf = &substream->dma_buffer;
388
size_t size = dma_hardware.buffer_bytes_max;
390
pr_debug("Entered %s\n", __func__);
392
buf->dev.type = SNDRV_DMA_TYPE_DEV;
393
buf->dev.dev = pcm->card->dev;
394
buf->private_data = NULL;
395
buf->area = dma_alloc_writecombine(pcm->card->dev, size,
396
&buf->addr, GFP_KERNEL);
403
static void dma_free_dma_buffers(struct snd_pcm *pcm)
405
struct snd_pcm_substream *substream;
406
struct snd_dma_buffer *buf;
409
pr_debug("Entered %s\n", __func__);
411
for (stream = 0; stream < 2; stream++) {
412
substream = pcm->streams[stream].substream;
416
buf = &substream->dma_buffer;
420
dma_free_writecombine(pcm->card->dev, buf->bytes,
421
buf->area, buf->addr);
426
static u64 dma_mask = DMA_BIT_MASK(32);
428
static int dma_new(struct snd_card *card,
429
struct snd_soc_dai *dai, struct snd_pcm *pcm)
433
pr_debug("Entered %s\n", __func__);
435
if (!card->dev->dma_mask)
436
card->dev->dma_mask = &dma_mask;
437
if (!card->dev->coherent_dma_mask)
438
card->dev->coherent_dma_mask = 0xffffffff;
440
if (dai->driver->playback.channels_min) {
441
ret = preallocate_dma_buffer(pcm,
442
SNDRV_PCM_STREAM_PLAYBACK);
447
if (dai->driver->capture.channels_min) {
448
ret = preallocate_dma_buffer(pcm,
449
SNDRV_PCM_STREAM_CAPTURE);
457
static struct snd_soc_platform_driver samsung_asoc_platform = {
460
.pcm_free = dma_free_dma_buffers,
463
static int __devinit samsung_asoc_platform_probe(struct platform_device *pdev)
465
return snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);
468
static int __devexit samsung_asoc_platform_remove(struct platform_device *pdev)
470
snd_soc_unregister_platform(&pdev->dev);
474
static struct platform_driver asoc_dma_driver = {
476
.name = "samsung-audio",
477
.owner = THIS_MODULE,
480
.probe = samsung_asoc_platform_probe,
481
.remove = __devexit_p(samsung_asoc_platform_remove),
484
static int __init samsung_asoc_init(void)
486
return platform_driver_register(&asoc_dma_driver);
488
module_init(samsung_asoc_init);
490
static void __exit samsung_asoc_exit(void)
492
platform_driver_unregister(&asoc_dma_driver);
494
module_exit(samsung_asoc_exit);
496
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
497
MODULE_DESCRIPTION("Samsung ASoC DMA Driver");
498
MODULE_LICENSE("GPL");
499
MODULE_ALIAS("platform:samsung-audio");