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

« back to all changes in this revision

Viewing changes to alsa-kernel/soc/s3c24xx/s3c-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
 
 * s3c-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/module.h>
18
 
#include <linux/init.h>
19
 
#include <linux/io.h>
20
 
#include <linux/platform_device.h>
21
 
#include <linux/slab.h>
22
 
#include <linux/dma-mapping.h>
23
 
 
24
 
#include <sound/core.h>
25
 
#include <sound/pcm.h>
26
 
#include <sound/pcm_params.h>
27
 
#include <sound/soc.h>
28
 
 
29
 
#include <asm/dma.h>
30
 
#include <mach/hardware.h>
31
 
#include <mach/dma.h>
32
 
 
33
 
#include "s3c-dma.h"
34
 
 
35
 
static const struct snd_pcm_hardware s3c_dma_hardware = {
36
 
        .info                   = SNDRV_PCM_INFO_INTERLEAVED |
37
 
                                    SNDRV_PCM_INFO_BLOCK_TRANSFER |
38
 
                                    SNDRV_PCM_INFO_MMAP |
39
 
                                    SNDRV_PCM_INFO_MMAP_VALID |
40
 
                                    SNDRV_PCM_INFO_PAUSE |
41
 
                                    SNDRV_PCM_INFO_RESUME,
42
 
        .formats                = SNDRV_PCM_FMTBIT_S16_LE |
43
 
                                    SNDRV_PCM_FMTBIT_U16_LE |
44
 
                                    SNDRV_PCM_FMTBIT_U8 |
45
 
                                    SNDRV_PCM_FMTBIT_S8,
46
 
        .channels_min           = 2,
47
 
        .channels_max           = 2,
48
 
        .buffer_bytes_max       = 128*1024,
49
 
        .period_bytes_min       = PAGE_SIZE,
50
 
        .period_bytes_max       = PAGE_SIZE*2,
51
 
        .periods_min            = 2,
52
 
        .periods_max            = 128,
53
 
        .fifo_size              = 32,
54
 
};
55
 
 
56
 
struct s3c24xx_runtime_data {
57
 
        spinlock_t lock;
58
 
        int state;
59
 
        unsigned int dma_loaded;
60
 
        unsigned int dma_limit;
61
 
        unsigned int dma_period;
62
 
        dma_addr_t dma_start;
63
 
        dma_addr_t dma_pos;
64
 
        dma_addr_t dma_end;
65
 
        struct s3c_dma_params *params;
66
 
};
67
 
 
68
 
/* s3c_dma_enqueue
69
 
 *
70
 
 * place a dma buffer onto the queue for the dma system
71
 
 * to handle.
72
 
*/
73
 
static void s3c_dma_enqueue(struct snd_pcm_substream *substream)
74
 
{
75
 
        struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
76
 
        dma_addr_t pos = prtd->dma_pos;
77
 
        unsigned int limit;
78
 
        int ret;
79
 
 
80
 
        pr_debug("Entered %s\n", __func__);
81
 
 
82
 
        if (s3c_dma_has_circular())
83
 
                limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
84
 
        else
85
 
                limit = prtd->dma_limit;
86
 
 
87
 
        pr_debug("%s: loaded %d, limit %d\n",
88
 
                                __func__, prtd->dma_loaded, limit);
89
 
 
90
 
        while (prtd->dma_loaded < limit) {
91
 
                unsigned long len = prtd->dma_period;
92
 
 
93
 
                pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
94
 
 
95
 
                if ((pos + len) > prtd->dma_end) {
96
 
                        len  = prtd->dma_end - pos;
97
 
                        pr_debug(KERN_DEBUG "%s: corrected dma len %ld\n",
98
 
                               __func__, len);
99
 
                }
100
 
 
101
 
                ret = s3c2410_dma_enqueue(prtd->params->channel,
102
 
                        substream, pos, len);
103
 
 
104
 
                if (ret == 0) {
105
 
                        prtd->dma_loaded++;
106
 
                        pos += prtd->dma_period;
107
 
                        if (pos >= prtd->dma_end)
108
 
                                pos = prtd->dma_start;
109
 
                } else
110
 
                        break;
111
 
        }
112
 
 
113
 
        prtd->dma_pos = pos;
114
 
}
115
 
 
116
 
static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel,
117
 
                                void *dev_id, int size,
118
 
                                enum s3c2410_dma_buffresult result)
119
 
{
120
 
        struct snd_pcm_substream *substream = dev_id;
121
 
        struct s3c24xx_runtime_data *prtd;
122
 
 
123
 
        pr_debug("Entered %s\n", __func__);
124
 
 
125
 
        if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR)
126
 
                return;
127
 
 
128
 
        prtd = substream->runtime->private_data;
129
 
 
130
 
        if (substream)
131
 
                snd_pcm_period_elapsed(substream);
132
 
 
133
 
        spin_lock(&prtd->lock);
134
 
        if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) {
135
 
                prtd->dma_loaded--;
136
 
                s3c_dma_enqueue(substream);
137
 
        }
138
 
 
139
 
        spin_unlock(&prtd->lock);
140
 
}
141
 
 
142
 
static int s3c_dma_hw_params(struct snd_pcm_substream *substream,
143
 
        struct snd_pcm_hw_params *params)
144
 
{
145
 
        struct snd_pcm_runtime *runtime = substream->runtime;
146
 
        struct s3c24xx_runtime_data *prtd = runtime->private_data;
147
 
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
148
 
        unsigned long totbytes = params_buffer_bytes(params);
149
 
        struct s3c_dma_params *dma =
150
 
                snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
151
 
        int ret = 0;
152
 
 
153
 
 
154
 
        pr_debug("Entered %s\n", __func__);
155
 
 
156
 
        /* return if this is a bufferless transfer e.g.
157
 
         * codec <--> BT codec or GSM modem -- lg FIXME */
158
 
        if (!dma)
159
 
                return 0;
160
 
 
161
 
        /* this may get called several times by oss emulation
162
 
         * with different params -HW */
163
 
        if (prtd->params == NULL) {
164
 
                /* prepare DMA */
165
 
                prtd->params = dma;
166
 
 
167
 
                pr_debug("params %p, client %p, channel %d\n", prtd->params,
168
 
                        prtd->params->client, prtd->params->channel);
169
 
 
170
 
                ret = s3c2410_dma_request(prtd->params->channel,
171
 
                                          prtd->params->client, NULL);
172
 
 
173
 
                if (ret < 0) {
174
 
                        printk(KERN_ERR "failed to get dma channel\n");
175
 
                        return ret;
176
 
                }
177
 
 
178
 
                /* use the circular buffering if we have it available. */
179
 
                if (s3c_dma_has_circular())
180
 
                        s3c2410_dma_setflags(prtd->params->channel,
181
 
                                             S3C2410_DMAF_CIRCULAR);
182
 
        }
183
 
 
184
 
        s3c2410_dma_set_buffdone_fn(prtd->params->channel,
185
 
                                    s3c24xx_audio_buffdone);
186
 
 
187
 
        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
188
 
 
189
 
        runtime->dma_bytes = totbytes;
190
 
 
191
 
        spin_lock_irq(&prtd->lock);
192
 
        prtd->dma_loaded = 0;
193
 
        prtd->dma_limit = runtime->hw.periods_min;
194
 
        prtd->dma_period = params_period_bytes(params);
195
 
        prtd->dma_start = runtime->dma_addr;
196
 
        prtd->dma_pos = prtd->dma_start;
197
 
        prtd->dma_end = prtd->dma_start + totbytes;
198
 
        spin_unlock_irq(&prtd->lock);
199
 
 
200
 
        return 0;
201
 
}
202
 
 
203
 
static int s3c_dma_hw_free(struct snd_pcm_substream *substream)
204
 
{
205
 
        struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
206
 
 
207
 
        pr_debug("Entered %s\n", __func__);
208
 
 
209
 
        /* TODO - do we need to ensure DMA flushed */
210
 
        snd_pcm_set_runtime_buffer(substream, NULL);
211
 
 
212
 
        if (prtd->params) {
213
 
                s3c2410_dma_free(prtd->params->channel, prtd->params->client);
214
 
                prtd->params = NULL;
215
 
        }
216
 
 
217
 
        return 0;
218
 
}
219
 
 
220
 
static int s3c_dma_prepare(struct snd_pcm_substream *substream)
221
 
{
222
 
        struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
223
 
        int ret = 0;
224
 
 
225
 
        pr_debug("Entered %s\n", __func__);
226
 
 
227
 
        /* return if this is a bufferless transfer e.g.
228
 
         * codec <--> BT codec or GSM modem -- lg FIXME */
229
 
        if (!prtd->params)
230
 
                return 0;
231
 
 
232
 
        /* channel needs configuring for mem=>device, increment memory addr,
233
 
         * sync to pclk, half-word transfers to the IIS-FIFO. */
234
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
235
 
                s3c2410_dma_devconfig(prtd->params->channel,
236
 
                                      S3C2410_DMASRC_MEM,
237
 
                                      prtd->params->dma_addr);
238
 
        } else {
239
 
                s3c2410_dma_devconfig(prtd->params->channel,
240
 
                                      S3C2410_DMASRC_HW,
241
 
                                      prtd->params->dma_addr);
242
 
        }
243
 
 
244
 
        s3c2410_dma_config(prtd->params->channel,
245
 
                           prtd->params->dma_size);
246
 
 
247
 
        /* flush the DMA channel */
248
 
        s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH);
249
 
        prtd->dma_loaded = 0;
250
 
        prtd->dma_pos = prtd->dma_start;
251
 
 
252
 
        /* enqueue dma buffers */
253
 
        s3c_dma_enqueue(substream);
254
 
 
255
 
        return ret;
256
 
}
257
 
 
258
 
static int s3c_dma_trigger(struct snd_pcm_substream *substream, int cmd)
259
 
{
260
 
        struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
261
 
        int ret = 0;
262
 
 
263
 
        pr_debug("Entered %s\n", __func__);
264
 
 
265
 
        spin_lock(&prtd->lock);
266
 
 
267
 
        switch (cmd) {
268
 
        case SNDRV_PCM_TRIGGER_START:
269
 
        case SNDRV_PCM_TRIGGER_RESUME:
270
 
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
271
 
                prtd->state |= ST_RUNNING;
272
 
                s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START);
273
 
                break;
274
 
 
275
 
        case SNDRV_PCM_TRIGGER_STOP:
276
 
        case SNDRV_PCM_TRIGGER_SUSPEND:
277
 
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
278
 
                prtd->state &= ~ST_RUNNING;
279
 
                s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STOP);
280
 
                break;
281
 
 
282
 
        default:
283
 
                ret = -EINVAL;
284
 
                break;
285
 
        }
286
 
 
287
 
        spin_unlock(&prtd->lock);
288
 
 
289
 
        return ret;
290
 
}
291
 
 
292
 
static snd_pcm_uframes_t
293
 
s3c_dma_pointer(struct snd_pcm_substream *substream)
294
 
{
295
 
        struct snd_pcm_runtime *runtime = substream->runtime;
296
 
        struct s3c24xx_runtime_data *prtd = runtime->private_data;
297
 
        unsigned long res;
298
 
        dma_addr_t src, dst;
299
 
 
300
 
        pr_debug("Entered %s\n", __func__);
301
 
 
302
 
        spin_lock(&prtd->lock);
303
 
        s3c2410_dma_getposition(prtd->params->channel, &src, &dst);
304
 
 
305
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
306
 
                res = dst - prtd->dma_start;
307
 
        else
308
 
                res = src - prtd->dma_start;
309
 
 
310
 
        spin_unlock(&prtd->lock);
311
 
 
312
 
        pr_debug("Pointer %x %x\n", src, dst);
313
 
 
314
 
        /* we seem to be getting the odd error from the pcm library due
315
 
         * to out-of-bounds pointers. this is maybe due to the dma engine
316
 
         * not having loaded the new values for the channel before being
317
 
         * callled... (todo - fix )
318
 
         */
319
 
 
320
 
        if (res >= snd_pcm_lib_buffer_bytes(substream)) {
321
 
                if (res == snd_pcm_lib_buffer_bytes(substream))
322
 
                        res = 0;
323
 
        }
324
 
 
325
 
        return bytes_to_frames(substream->runtime, res);
326
 
}
327
 
 
328
 
static int s3c_dma_open(struct snd_pcm_substream *substream)
329
 
{
330
 
        struct snd_pcm_runtime *runtime = substream->runtime;
331
 
        struct s3c24xx_runtime_data *prtd;
332
 
 
333
 
        pr_debug("Entered %s\n", __func__);
334
 
 
335
 
        snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
336
 
        snd_soc_set_runtime_hwparams(substream, &s3c_dma_hardware);
337
 
 
338
 
        prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL);
339
 
        if (prtd == NULL)
340
 
                return -ENOMEM;
341
 
 
342
 
        spin_lock_init(&prtd->lock);
343
 
 
344
 
        runtime->private_data = prtd;
345
 
        return 0;
346
 
}
347
 
 
348
 
static int s3c_dma_close(struct snd_pcm_substream *substream)
349
 
{
350
 
        struct snd_pcm_runtime *runtime = substream->runtime;
351
 
        struct s3c24xx_runtime_data *prtd = runtime->private_data;
352
 
 
353
 
        pr_debug("Entered %s\n", __func__);
354
 
 
355
 
        if (!prtd)
356
 
                pr_debug("s3c_dma_close called with prtd == NULL\n");
357
 
 
358
 
        kfree(prtd);
359
 
 
360
 
        return 0;
361
 
}
362
 
 
363
 
static int s3c_dma_mmap(struct snd_pcm_substream *substream,
364
 
        struct vm_area_struct *vma)
365
 
{
366
 
        struct snd_pcm_runtime *runtime = substream->runtime;
367
 
 
368
 
        pr_debug("Entered %s\n", __func__);
369
 
 
370
 
        return dma_mmap_writecombine(substream->pcm->card->dev, vma,
371
 
                                     runtime->dma_area,
372
 
                                     runtime->dma_addr,
373
 
                                     runtime->dma_bytes);
374
 
}
375
 
 
376
 
static struct snd_pcm_ops s3c_dma_ops = {
377
 
        .open           = s3c_dma_open,
378
 
        .close          = s3c_dma_close,
379
 
        .ioctl          = snd_pcm_lib_ioctl,
380
 
        .hw_params      = s3c_dma_hw_params,
381
 
        .hw_free        = s3c_dma_hw_free,
382
 
        .prepare        = s3c_dma_prepare,
383
 
        .trigger        = s3c_dma_trigger,
384
 
        .pointer        = s3c_dma_pointer,
385
 
        .mmap           = s3c_dma_mmap,
386
 
};
387
 
 
388
 
static int s3c_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
389
 
{
390
 
        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
391
 
        struct snd_dma_buffer *buf = &substream->dma_buffer;
392
 
        size_t size = s3c_dma_hardware.buffer_bytes_max;
393
 
 
394
 
        pr_debug("Entered %s\n", __func__);
395
 
 
396
 
        buf->dev.type = SNDRV_DMA_TYPE_DEV;
397
 
        buf->dev.dev = pcm->card->dev;
398
 
        buf->private_data = NULL;
399
 
        buf->area = dma_alloc_writecombine(pcm->card->dev, size,
400
 
                                           &buf->addr, GFP_KERNEL);
401
 
        if (!buf->area)
402
 
                return -ENOMEM;
403
 
        buf->bytes = size;
404
 
        return 0;
405
 
}
406
 
 
407
 
static void s3c_dma_free_dma_buffers(struct snd_pcm *pcm)
408
 
{
409
 
        struct snd_pcm_substream *substream;
410
 
        struct snd_dma_buffer *buf;
411
 
        int stream;
412
 
 
413
 
        pr_debug("Entered %s\n", __func__);
414
 
 
415
 
        for (stream = 0; stream < 2; stream++) {
416
 
                substream = pcm->streams[stream].substream;
417
 
                if (!substream)
418
 
                        continue;
419
 
 
420
 
                buf = &substream->dma_buffer;
421
 
                if (!buf->area)
422
 
                        continue;
423
 
 
424
 
                dma_free_writecombine(pcm->card->dev, buf->bytes,
425
 
                                      buf->area, buf->addr);
426
 
                buf->area = NULL;
427
 
        }
428
 
}
429
 
 
430
 
static u64 s3c_dma_mask = DMA_BIT_MASK(32);
431
 
 
432
 
static int s3c_dma_new(struct snd_card *card,
433
 
        struct snd_soc_dai *dai, struct snd_pcm *pcm)
434
 
{
435
 
        int ret = 0;
436
 
 
437
 
        pr_debug("Entered %s\n", __func__);
438
 
 
439
 
        if (!card->dev->dma_mask)
440
 
                card->dev->dma_mask = &s3c_dma_mask;
441
 
        if (!card->dev->coherent_dma_mask)
442
 
                card->dev->coherent_dma_mask = 0xffffffff;
443
 
 
444
 
        if (dai->playback.channels_min) {
445
 
                ret = s3c_preallocate_dma_buffer(pcm,
446
 
                        SNDRV_PCM_STREAM_PLAYBACK);
447
 
                if (ret)
448
 
                        goto out;
449
 
        }
450
 
 
451
 
        if (dai->capture.channels_min) {
452
 
                ret = s3c_preallocate_dma_buffer(pcm,
453
 
                        SNDRV_PCM_STREAM_CAPTURE);
454
 
                if (ret)
455
 
                        goto out;
456
 
        }
457
 
 out:
458
 
        return ret;
459
 
}
460
 
 
461
 
struct snd_soc_platform s3c24xx_soc_platform = {
462
 
        .name           = "s3c24xx-audio",
463
 
        .pcm_ops        = &s3c_dma_ops,
464
 
        .pcm_new        = s3c_dma_new,
465
 
        .pcm_free       = s3c_dma_free_dma_buffers,
466
 
};
467
 
EXPORT_SYMBOL_GPL(s3c24xx_soc_platform);
468
 
 
469
 
static int __init s3c24xx_soc_platform_init(void)
470
 
{
471
 
        return snd_soc_register_platform(&s3c24xx_soc_platform);
472
 
}
473
 
module_init(s3c24xx_soc_platform_init);
474
 
 
475
 
static void __exit s3c24xx_soc_platform_exit(void)
476
 
{
477
 
        snd_soc_unregister_platform(&s3c24xx_soc_platform);
478
 
}
479
 
module_exit(s3c24xx_soc_platform_exit);
480
 
 
481
 
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
482
 
MODULE_DESCRIPTION("Samsung S3C Audio DMA module");
483
 
MODULE_LICENSE("GPL");