~ubuntu-branches/ubuntu/precise/alsa-driver/precise

« back to all changes in this revision

Viewing changes to alsa-kernel/soc/samsung/dma.c

  • Committer: Bazaar Package Importer
  • Author(s): Luke Yelavich
  • Date: 2011-02-21 18:06:40 UTC
  • mfrom: (1.1.15 upstream)
  • Revision ID: james.westby@ubuntu.com-20110221180640-a8p2yxtvgf7xbxub
Tags: 1.0.24+dfsg-0ubuntu1
* New upstream release
* Refreshed patches:
  - distinguish_kernel_makefile_and_source_dirs.patch
  - debian_dfsg_configure.patch
* debian/control: Update Vcs-bzr field to point to new branch location

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * dma.c  --  ALSA Soc Audio Layer
 
3
 *
 
4
 * (c) 2006 Wolfson Microelectronics PLC.
 
5
 * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
 
6
 *
 
7
 * Copyright 2004-2005 Simtec Electronics
 
8
 *      http://armlinux.simtec.co.uk/
 
9
 *      Ben Dooks <ben@simtec.co.uk>
 
10
 *
 
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.
 
15
 */
 
16
 
 
17
#include <linux/slab.h>
 
18
#include <linux/dma-mapping.h>
 
19
 
 
20
#include <sound/soc.h>
 
21
#include <sound/pcm_params.h>
 
22
 
 
23
#include <asm/dma.h>
 
24
#include <mach/hardware.h>
 
25
#include <mach/dma.h>
 
26
 
 
27
#include "dma.h"
 
28
 
 
29
#define ST_RUNNING              (1<<0)
 
30
#define ST_OPENED               (1<<1)
 
31
 
 
32
static const struct snd_pcm_hardware dma_hardware = {
 
33
        .info                   = SNDRV_PCM_INFO_INTERLEAVED |
 
34
                                    SNDRV_PCM_INFO_BLOCK_TRANSFER |
 
35
                                    SNDRV_PCM_INFO_MMAP |
 
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 |
 
41
                                    SNDRV_PCM_FMTBIT_U8 |
 
42
                                    SNDRV_PCM_FMTBIT_S8,
 
43
        .channels_min           = 2,
 
44
        .channels_max           = 2,
 
45
        .buffer_bytes_max       = 128*1024,
 
46
        .period_bytes_min       = PAGE_SIZE,
 
47
        .period_bytes_max       = PAGE_SIZE*2,
 
48
        .periods_min            = 2,
 
49
        .periods_max            = 128,
 
50
        .fifo_size              = 32,
 
51
};
 
52
 
 
53
struct runtime_data {
 
54
        spinlock_t lock;
 
55
        int state;
 
56
        unsigned int dma_loaded;
 
57
        unsigned int dma_limit;
 
58
        unsigned int dma_period;
 
59
        dma_addr_t dma_start;
 
60
        dma_addr_t dma_pos;
 
61
        dma_addr_t dma_end;
 
62
        struct s3c_dma_params *params;
 
63
};
 
64
 
 
65
/* dma_enqueue
 
66
 *
 
67
 * place a dma buffer onto the queue for the dma system
 
68
 * to handle.
 
69
*/
 
70
static void dma_enqueue(struct snd_pcm_substream *substream)
 
71
{
 
72
        struct runtime_data *prtd = substream->runtime->private_data;
 
73
        dma_addr_t pos = prtd->dma_pos;
 
74
        unsigned int limit;
 
75
        int ret;
 
76
 
 
77
        pr_debug("Entered %s\n", __func__);
 
78
 
 
79
        if (s3c_dma_has_circular())
 
80
                limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
 
81
        else
 
82
                limit = prtd->dma_limit;
 
83
 
 
84
        pr_debug("%s: loaded %d, limit %d\n",
 
85
                                __func__, prtd->dma_loaded, limit);
 
86
 
 
87
        while (prtd->dma_loaded < limit) {
 
88
                unsigned long len = prtd->dma_period;
 
89
 
 
90
                pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
 
91
 
 
92
                if ((pos + len) > prtd->dma_end) {
 
93
                        len  = prtd->dma_end - pos;
 
94
                        pr_debug("%s: corrected dma len %ld\n", __func__, len);
 
95
                }
 
96
 
 
97
                ret = s3c2410_dma_enqueue(prtd->params->channel,
 
98
                        substream, pos, len);
 
99
 
 
100
                if (ret == 0) {
 
101
                        prtd->dma_loaded++;
 
102
                        pos += prtd->dma_period;
 
103
                        if (pos >= prtd->dma_end)
 
104
                                pos = prtd->dma_start;
 
105
                } else
 
106
                        break;
 
107
        }
 
108
 
 
109
        prtd->dma_pos = pos;
 
110
}
 
111
 
 
112
static void audio_buffdone(struct s3c2410_dma_chan *channel,
 
113
                                void *dev_id, int size,
 
114
                                enum s3c2410_dma_buffresult result)
 
115
{
 
116
        struct snd_pcm_substream *substream = dev_id;
 
117
        struct runtime_data *prtd;
 
118
 
 
119
        pr_debug("Entered %s\n", __func__);
 
120
 
 
121
        if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR)
 
122
                return;
 
123
 
 
124
        prtd = substream->runtime->private_data;
 
125
 
 
126
        if (substream)
 
127
                snd_pcm_period_elapsed(substream);
 
128
 
 
129
        spin_lock(&prtd->lock);
 
130
        if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) {
 
131
                prtd->dma_loaded--;
 
132
                dma_enqueue(substream);
 
133
        }
 
134
 
 
135
        spin_unlock(&prtd->lock);
 
136
}
 
137
 
 
138
static int dma_hw_params(struct snd_pcm_substream *substream,
 
139
        struct snd_pcm_hw_params *params)
 
140
{
 
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);
 
147
        int ret = 0;
 
148
 
 
149
 
 
150
        pr_debug("Entered %s\n", __func__);
 
151
 
 
152
        /* return if this is a bufferless transfer e.g.
 
153
         * codec <--> BT codec or GSM modem -- lg FIXME */
 
154
        if (!dma)
 
155
                return 0;
 
156
 
 
157
        /* this may get called several times by oss emulation
 
158
         * with different params -HW */
 
159
        if (prtd->params == NULL) {
 
160
                /* prepare DMA */
 
161
                prtd->params = dma;
 
162
 
 
163
                pr_debug("params %p, client %p, channel %d\n", prtd->params,
 
164
                        prtd->params->client, prtd->params->channel);
 
165
 
 
166
                ret = s3c2410_dma_request(prtd->params->channel,
 
167
                                          prtd->params->client, NULL);
 
168
 
 
169
                if (ret < 0) {
 
170
                        printk(KERN_ERR "failed to get dma channel\n");
 
171
                        return ret;
 
172
                }
 
173
 
 
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);
 
178
        }
 
179
 
 
180
        s3c2410_dma_set_buffdone_fn(prtd->params->channel,
 
181
                                    audio_buffdone);
 
182
 
 
183
        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 
184
 
 
185
        runtime->dma_bytes = totbytes;
 
186
 
 
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);
 
195
 
 
196
        return 0;
 
197
}
 
198
 
 
199
static int dma_hw_free(struct snd_pcm_substream *substream)
 
200
{
 
201
        struct runtime_data *prtd = substream->runtime->private_data;
 
202
 
 
203
        pr_debug("Entered %s\n", __func__);
 
204
 
 
205
        /* TODO - do we need to ensure DMA flushed */
 
206
        snd_pcm_set_runtime_buffer(substream, NULL);
 
207
 
 
208
        if (prtd->params) {
 
209
                s3c2410_dma_free(prtd->params->channel, prtd->params->client);
 
210
                prtd->params = NULL;
 
211
        }
 
212
 
 
213
        return 0;
 
214
}
 
215
 
 
216
static int dma_prepare(struct snd_pcm_substream *substream)
 
217
{
 
218
        struct runtime_data *prtd = substream->runtime->private_data;
 
219
        int ret = 0;
 
220
 
 
221
        pr_debug("Entered %s\n", __func__);
 
222
 
 
223
        /* return if this is a bufferless transfer e.g.
 
224
         * codec <--> BT codec or GSM modem -- lg FIXME */
 
225
        if (!prtd->params)
 
226
                return 0;
 
227
 
 
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,
 
232
                                      S3C2410_DMASRC_MEM,
 
233
                                      prtd->params->dma_addr);
 
234
        } else {
 
235
                s3c2410_dma_devconfig(prtd->params->channel,
 
236
                                      S3C2410_DMASRC_HW,
 
237
                                      prtd->params->dma_addr);
 
238
        }
 
239
 
 
240
        s3c2410_dma_config(prtd->params->channel,
 
241
                           prtd->params->dma_size);
 
242
 
 
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;
 
247
 
 
248
        /* enqueue dma buffers */
 
249
        dma_enqueue(substream);
 
250
 
 
251
        return ret;
 
252
}
 
253
 
 
254
static int dma_trigger(struct snd_pcm_substream *substream, int cmd)
 
255
{
 
256
        struct runtime_data *prtd = substream->runtime->private_data;
 
257
        int ret = 0;
 
258
 
 
259
        pr_debug("Entered %s\n", __func__);
 
260
 
 
261
        spin_lock(&prtd->lock);
 
262
 
 
263
        switch (cmd) {
 
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);
 
269
                break;
 
270
 
 
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);
 
276
                break;
 
277
 
 
278
        default:
 
279
                ret = -EINVAL;
 
280
                break;
 
281
        }
 
282
 
 
283
        spin_unlock(&prtd->lock);
 
284
 
 
285
        return ret;
 
286
}
 
287
 
 
288
static snd_pcm_uframes_t
 
289
dma_pointer(struct snd_pcm_substream *substream)
 
290
{
 
291
        struct snd_pcm_runtime *runtime = substream->runtime;
 
292
        struct runtime_data *prtd = runtime->private_data;
 
293
        unsigned long res;
 
294
        dma_addr_t src, dst;
 
295
 
 
296
        pr_debug("Entered %s\n", __func__);
 
297
 
 
298
        spin_lock(&prtd->lock);
 
299
        s3c2410_dma_getposition(prtd->params->channel, &src, &dst);
 
300
 
 
301
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 
302
                res = dst - prtd->dma_start;
 
303
        else
 
304
                res = src - prtd->dma_start;
 
305
 
 
306
        spin_unlock(&prtd->lock);
 
307
 
 
308
        pr_debug("Pointer %x %x\n", src, dst);
 
309
 
 
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 )
 
314
         */
 
315
 
 
316
        if (res >= snd_pcm_lib_buffer_bytes(substream)) {
 
317
                if (res == snd_pcm_lib_buffer_bytes(substream))
 
318
                        res = 0;
 
319
        }
 
320
 
 
321
        return bytes_to_frames(substream->runtime, res);
 
322
}
 
323
 
 
324
static int dma_open(struct snd_pcm_substream *substream)
 
325
{
 
326
        struct snd_pcm_runtime *runtime = substream->runtime;
 
327
        struct runtime_data *prtd;
 
328
 
 
329
        pr_debug("Entered %s\n", __func__);
 
330
 
 
331
        snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
 
332
        snd_soc_set_runtime_hwparams(substream, &dma_hardware);
 
333
 
 
334
        prtd = kzalloc(sizeof(struct runtime_data), GFP_KERNEL);
 
335
        if (prtd == NULL)
 
336
                return -ENOMEM;
 
337
 
 
338
        spin_lock_init(&prtd->lock);
 
339
 
 
340
        runtime->private_data = prtd;
 
341
        return 0;
 
342
}
 
343
 
 
344
static int dma_close(struct snd_pcm_substream *substream)
 
345
{
 
346
        struct snd_pcm_runtime *runtime = substream->runtime;
 
347
        struct runtime_data *prtd = runtime->private_data;
 
348
 
 
349
        pr_debug("Entered %s\n", __func__);
 
350
 
 
351
        if (!prtd)
 
352
                pr_debug("dma_close called with prtd == NULL\n");
 
353
 
 
354
        kfree(prtd);
 
355
 
 
356
        return 0;
 
357
}
 
358
 
 
359
static int dma_mmap(struct snd_pcm_substream *substream,
 
360
        struct vm_area_struct *vma)
 
361
{
 
362
        struct snd_pcm_runtime *runtime = substream->runtime;
 
363
 
 
364
        pr_debug("Entered %s\n", __func__);
 
365
 
 
366
        return dma_mmap_writecombine(substream->pcm->card->dev, vma,
 
367
                                     runtime->dma_area,
 
368
                                     runtime->dma_addr,
 
369
                                     runtime->dma_bytes);
 
370
}
 
371
 
 
372
static struct snd_pcm_ops dma_ops = {
 
373
        .open           = dma_open,
 
374
        .close          = dma_close,
 
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,
 
381
        .mmap           = dma_mmap,
 
382
};
 
383
 
 
384
static int preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 
385
{
 
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;
 
389
 
 
390
        pr_debug("Entered %s\n", __func__);
 
391
 
 
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);
 
397
        if (!buf->area)
 
398
                return -ENOMEM;
 
399
        buf->bytes = size;
 
400
        return 0;
 
401
}
 
402
 
 
403
static void dma_free_dma_buffers(struct snd_pcm *pcm)
 
404
{
 
405
        struct snd_pcm_substream *substream;
 
406
        struct snd_dma_buffer *buf;
 
407
        int stream;
 
408
 
 
409
        pr_debug("Entered %s\n", __func__);
 
410
 
 
411
        for (stream = 0; stream < 2; stream++) {
 
412
                substream = pcm->streams[stream].substream;
 
413
                if (!substream)
 
414
                        continue;
 
415
 
 
416
                buf = &substream->dma_buffer;
 
417
                if (!buf->area)
 
418
                        continue;
 
419
 
 
420
                dma_free_writecombine(pcm->card->dev, buf->bytes,
 
421
                                      buf->area, buf->addr);
 
422
                buf->area = NULL;
 
423
        }
 
424
}
 
425
 
 
426
static u64 dma_mask = DMA_BIT_MASK(32);
 
427
 
 
428
static int dma_new(struct snd_card *card,
 
429
        struct snd_soc_dai *dai, struct snd_pcm *pcm)
 
430
{
 
431
        int ret = 0;
 
432
 
 
433
        pr_debug("Entered %s\n", __func__);
 
434
 
 
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;
 
439
 
 
440
        if (dai->driver->playback.channels_min) {
 
441
                ret = preallocate_dma_buffer(pcm,
 
442
                        SNDRV_PCM_STREAM_PLAYBACK);
 
443
                if (ret)
 
444
                        goto out;
 
445
        }
 
446
 
 
447
        if (dai->driver->capture.channels_min) {
 
448
                ret = preallocate_dma_buffer(pcm,
 
449
                        SNDRV_PCM_STREAM_CAPTURE);
 
450
                if (ret)
 
451
                        goto out;
 
452
        }
 
453
out:
 
454
        return ret;
 
455
}
 
456
 
 
457
static struct snd_soc_platform_driver samsung_asoc_platform = {
 
458
        .ops            = &dma_ops,
 
459
        .pcm_new        = dma_new,
 
460
        .pcm_free       = dma_free_dma_buffers,
 
461
};
 
462
 
 
463
static int __devinit samsung_asoc_platform_probe(struct platform_device *pdev)
 
464
{
 
465
        return snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);
 
466
}
 
467
 
 
468
static int __devexit samsung_asoc_platform_remove(struct platform_device *pdev)
 
469
{
 
470
        snd_soc_unregister_platform(&pdev->dev);
 
471
        return 0;
 
472
}
 
473
 
 
474
static struct platform_driver asoc_dma_driver = {
 
475
        .driver = {
 
476
                .name = "samsung-audio",
 
477
                .owner = THIS_MODULE,
 
478
        },
 
479
 
 
480
        .probe = samsung_asoc_platform_probe,
 
481
        .remove = __devexit_p(samsung_asoc_platform_remove),
 
482
};
 
483
 
 
484
static int __init samsung_asoc_init(void)
 
485
{
 
486
        return platform_driver_register(&asoc_dma_driver);
 
487
}
 
488
module_init(samsung_asoc_init);
 
489
 
 
490
static void __exit samsung_asoc_exit(void)
 
491
{
 
492
        platform_driver_unregister(&asoc_dma_driver);
 
493
}
 
494
module_exit(samsung_asoc_exit);
 
495
 
 
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");