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

« back to all changes in this revision

Viewing changes to alsa-kernel/soc/nuc900/nuc900-pcm.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
 * Copyright (c) 2010 Nuvoton technology corporation.
 
3
 *
 
4
 * Wan ZongShun <mcuos.com@gmail.com>
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation;version 2 of the License.
 
9
 *
 
10
 */
 
11
 
 
12
#include <linux/module.h>
 
13
#include <linux/init.h>
 
14
#include <linux/io.h>
 
15
#include <linux/platform_device.h>
 
16
#include <linux/slab.h>
 
17
#include <linux/dma-mapping.h>
 
18
 
 
19
#include <sound/core.h>
 
20
#include <sound/pcm.h>
 
21
#include <sound/pcm_params.h>
 
22
#include <sound/soc.h>
 
23
 
 
24
#include <mach/hardware.h>
 
25
 
 
26
#include "nuc900-audio.h"
 
27
 
 
28
static const struct snd_pcm_hardware nuc900_pcm_hardware = {
 
29
        .info                   = SNDRV_PCM_INFO_INTERLEAVED |
 
30
                                        SNDRV_PCM_INFO_BLOCK_TRANSFER |
 
31
                                        SNDRV_PCM_INFO_MMAP |
 
32
                                        SNDRV_PCM_INFO_MMAP_VALID |
 
33
                                        SNDRV_PCM_INFO_PAUSE |
 
34
                                        SNDRV_PCM_INFO_RESUME,
 
35
        .formats                = SNDRV_PCM_FMTBIT_S16_LE,
 
36
        .channels_min           = 1,
 
37
        .channels_max           = 2,
 
38
        .buffer_bytes_max       = 4*1024,
 
39
        .period_bytes_min       = 1*1024,
 
40
        .period_bytes_max       = 4*1024,
 
41
        .periods_min            = 1,
 
42
        .periods_max            = 1024,
 
43
};
 
44
 
 
45
static int nuc900_dma_hw_params(struct snd_pcm_substream *substream,
 
46
        struct snd_pcm_hw_params *params)
 
47
{
 
48
        struct snd_pcm_runtime *runtime = substream->runtime;
 
49
        struct nuc900_audio *nuc900_audio = runtime->private_data;
 
50
        unsigned long flags;
 
51
        int ret = 0;
 
52
 
 
53
        ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
 
54
        if (ret < 0)
 
55
                return ret;
 
56
 
 
57
        spin_lock_irqsave(&nuc900_audio->lock, flags);
 
58
 
 
59
        nuc900_audio->substream = substream;
 
60
        nuc900_audio->dma_addr[substream->stream] = runtime->dma_addr;
 
61
        nuc900_audio->buffersize[substream->stream] =
 
62
                                                params_buffer_bytes(params);
 
63
 
 
64
        spin_unlock_irqrestore(&nuc900_audio->lock, flags);
 
65
 
 
66
        return ret;
 
67
}
 
68
 
 
69
static void nuc900_update_dma_register(struct snd_pcm_substream *substream,
 
70
                                dma_addr_t dma_addr, size_t count)
 
71
{
 
72
        struct snd_pcm_runtime *runtime = substream->runtime;
 
73
        struct nuc900_audio *nuc900_audio = runtime->private_data;
 
74
        void __iomem *mmio_addr, *mmio_len;
 
75
 
 
76
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 
77
                mmio_addr = nuc900_audio->mmio + ACTL_PDSTB;
 
78
                mmio_len = nuc900_audio->mmio + ACTL_PDST_LENGTH;
 
79
        } else {
 
80
                mmio_addr = nuc900_audio->mmio + ACTL_RDSTB;
 
81
                mmio_len = nuc900_audio->mmio + ACTL_RDST_LENGTH;
 
82
        }
 
83
 
 
84
        AUDIO_WRITE(mmio_addr, dma_addr);
 
85
        AUDIO_WRITE(mmio_len, count);
 
86
}
 
87
 
 
88
static void nuc900_dma_start(struct snd_pcm_substream *substream)
 
89
{
 
90
        struct snd_pcm_runtime *runtime = substream->runtime;
 
91
        struct nuc900_audio *nuc900_audio = runtime->private_data;
 
92
        unsigned long val;
 
93
 
 
94
        val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
 
95
        val |= (T_DMA_IRQ | R_DMA_IRQ);
 
96
        AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
 
97
}
 
98
 
 
99
static void nuc900_dma_stop(struct snd_pcm_substream *substream)
 
100
{
 
101
        struct snd_pcm_runtime *runtime = substream->runtime;
 
102
        struct nuc900_audio *nuc900_audio = runtime->private_data;
 
103
        unsigned long val;
 
104
 
 
105
        val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
 
106
        val &= ~(T_DMA_IRQ | R_DMA_IRQ);
 
107
        AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
 
108
}
 
109
 
 
110
static irqreturn_t nuc900_dma_interrupt(int irq, void *dev_id)
 
111
{
 
112
        struct snd_pcm_substream *substream = dev_id;
 
113
        struct nuc900_audio *nuc900_audio = substream->runtime->private_data;
 
114
        unsigned long val;
 
115
 
 
116
        spin_lock(&nuc900_audio->lock);
 
117
 
 
118
        val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
 
119
 
 
120
        if (val & R_DMA_IRQ) {
 
121
                AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | R_DMA_IRQ);
 
122
 
 
123
                val = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
 
124
 
 
125
                if (val & R_DMA_MIDDLE_IRQ) {
 
126
                        val |= R_DMA_MIDDLE_IRQ;
 
127
                        AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
 
128
                }
 
129
 
 
130
                if (val & R_DMA_END_IRQ) {
 
131
                        val |= R_DMA_END_IRQ;
 
132
                        AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
 
133
                }
 
134
        } else if (val & T_DMA_IRQ) {
 
135
                AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | T_DMA_IRQ);
 
136
 
 
137
                val = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
 
138
 
 
139
                if (val & P_DMA_MIDDLE_IRQ) {
 
140
                        val |= P_DMA_MIDDLE_IRQ;
 
141
                        AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
 
142
                }
 
143
 
 
144
                if (val & P_DMA_END_IRQ) {
 
145
                        val |= P_DMA_END_IRQ;
 
146
                        AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
 
147
                }
 
148
        } else {
 
149
                dev_err(nuc900_audio->dev, "Wrong DMA interrupt status!\n");
 
150
                spin_unlock(&nuc900_audio->lock);
 
151
                return IRQ_HANDLED;
 
152
        }
 
153
 
 
154
        spin_unlock(&nuc900_audio->lock);
 
155
 
 
156
        snd_pcm_period_elapsed(substream);
 
157
 
 
158
        return IRQ_HANDLED;
 
159
}
 
160
 
 
161
static int nuc900_dma_hw_free(struct snd_pcm_substream *substream)
 
162
{
 
163
        snd_pcm_lib_free_pages(substream);
 
164
        return 0;
 
165
}
 
166
 
 
167
static int nuc900_dma_prepare(struct snd_pcm_substream *substream)
 
168
{
 
169
        struct snd_pcm_runtime *runtime = substream->runtime;
 
170
        struct nuc900_audio *nuc900_audio = runtime->private_data;
 
171
        unsigned long flags, val;
 
172
        int ret = 0;
 
173
 
 
174
        spin_lock_irqsave(&nuc900_audio->lock, flags);
 
175
 
 
176
        nuc900_update_dma_register(substream,
 
177
                                nuc900_audio->dma_addr[substream->stream],
 
178
                                nuc900_audio->buffersize[substream->stream]);
 
179
 
 
180
        val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
 
181
 
 
182
        switch (runtime->channels) {
 
183
        case 1:
 
184
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 
185
                        val &= ~(PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
 
186
                        val |= PLAY_RIGHT_CHNNEL;
 
187
                } else {
 
188
                        val &= ~(RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
 
189
                        val |= RECORD_RIGHT_CHNNEL;
 
190
                }
 
191
                AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
 
192
                break;
 
193
        case 2:
 
194
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 
195
                        val |= (PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
 
196
                else
 
197
                        val |= (RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
 
198
                AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
 
199
                break;
 
200
        default:
 
201
                ret = -EINVAL;
 
202
        }
 
203
        spin_unlock_irqrestore(&nuc900_audio->lock, flags);
 
204
        return ret;
 
205
}
 
206
 
 
207
static int nuc900_dma_trigger(struct snd_pcm_substream *substream, int cmd)
 
208
{
 
209
        int ret = 0;
 
210
 
 
211
        switch (cmd) {
 
212
        case SNDRV_PCM_TRIGGER_START:
 
213
        case SNDRV_PCM_TRIGGER_RESUME:
 
214
                nuc900_dma_start(substream);
 
215
                break;
 
216
 
 
217
        case SNDRV_PCM_TRIGGER_STOP:
 
218
        case SNDRV_PCM_TRIGGER_SUSPEND:
 
219
                nuc900_dma_stop(substream);
 
220
                break;
 
221
 
 
222
        default:
 
223
                ret = -EINVAL;
 
224
                break;
 
225
        }
 
226
 
 
227
        return ret;
 
228
}
 
229
 
 
230
int nuc900_dma_getposition(struct snd_pcm_substream *substream,
 
231
                                        dma_addr_t *src, dma_addr_t *dst)
 
232
{
 
233
        struct snd_pcm_runtime *runtime = substream->runtime;
 
234
        struct nuc900_audio *nuc900_audio = runtime->private_data;
 
235
 
 
236
        if (src != NULL)
 
237
                *src = AUDIO_READ(nuc900_audio->mmio + ACTL_PDSTC);
 
238
 
 
239
        if (dst != NULL)
 
240
                *dst = AUDIO_READ(nuc900_audio->mmio + ACTL_RDSTC);
 
241
 
 
242
        return 0;
 
243
}
 
244
 
 
245
static snd_pcm_uframes_t nuc900_dma_pointer(struct snd_pcm_substream *substream)
 
246
{
 
247
        struct snd_pcm_runtime *runtime = substream->runtime;
 
248
        dma_addr_t src, dst;
 
249
        unsigned long res;
 
250
 
 
251
        nuc900_dma_getposition(substream, &src, &dst);
 
252
 
 
253
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 
254
                res = dst - runtime->dma_addr;
 
255
        else
 
256
                res = src - runtime->dma_addr;
 
257
 
 
258
        return bytes_to_frames(substream->runtime, res);
 
259
}
 
260
 
 
261
static int nuc900_dma_open(struct snd_pcm_substream *substream)
 
262
{
 
263
        struct snd_pcm_runtime *runtime = substream->runtime;
 
264
        struct nuc900_audio *nuc900_audio;
 
265
 
 
266
        snd_soc_set_runtime_hwparams(substream, &nuc900_pcm_hardware);
 
267
 
 
268
        nuc900_audio = nuc900_ac97_data;
 
269
 
 
270
        if (request_irq(nuc900_audio->irq_num, nuc900_dma_interrupt,
 
271
                        IRQF_DISABLED, "nuc900-dma", substream))
 
272
                return -EBUSY;
 
273
 
 
274
        runtime->private_data = nuc900_audio;
 
275
 
 
276
        return 0;
 
277
}
 
278
 
 
279
static int nuc900_dma_close(struct snd_pcm_substream *substream)
 
280
{
 
281
        struct snd_pcm_runtime *runtime = substream->runtime;
 
282
        struct nuc900_audio *nuc900_audio = runtime->private_data;
 
283
 
 
284
        free_irq(nuc900_audio->irq_num, substream);
 
285
 
 
286
        return 0;
 
287
}
 
288
 
 
289
static int nuc900_dma_mmap(struct snd_pcm_substream *substream,
 
290
        struct vm_area_struct *vma)
 
291
{
 
292
        struct snd_pcm_runtime *runtime = substream->runtime;
 
293
 
 
294
        return dma_mmap_writecombine(substream->pcm->card->dev, vma,
 
295
                                        runtime->dma_area,
 
296
                                        runtime->dma_addr,
 
297
                                        runtime->dma_bytes);
 
298
}
 
299
 
 
300
static struct snd_pcm_ops nuc900_dma_ops = {
 
301
        .open           = nuc900_dma_open,
 
302
        .close          = nuc900_dma_close,
 
303
        .ioctl          = snd_pcm_lib_ioctl,
 
304
        .hw_params      = nuc900_dma_hw_params,
 
305
        .hw_free        = nuc900_dma_hw_free,
 
306
        .prepare        = nuc900_dma_prepare,
 
307
        .trigger        = nuc900_dma_trigger,
 
308
        .pointer        = nuc900_dma_pointer,
 
309
        .mmap           = nuc900_dma_mmap,
 
310
};
 
311
 
 
312
static void nuc900_dma_free_dma_buffers(struct snd_pcm *pcm)
 
313
{
 
314
        snd_pcm_lib_preallocate_free_for_all(pcm);
 
315
}
 
316
 
 
317
static u64 nuc900_pcm_dmamask = DMA_BIT_MASK(32);
 
318
static int nuc900_dma_new(struct snd_card *card,
 
319
        struct snd_soc_dai *dai, struct snd_pcm *pcm)
 
320
{
 
321
        if (!card->dev->dma_mask)
 
322
                card->dev->dma_mask = &nuc900_pcm_dmamask;
 
323
        if (!card->dev->coherent_dma_mask)
 
324
                card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
 
325
 
 
326
        snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
 
327
                card->dev, 4 * 1024, (4 * 1024) - 1);
 
328
 
 
329
        return 0;
 
330
}
 
331
 
 
332
static struct snd_soc_platform_driver nuc900_soc_platform = {
 
333
        .ops            = &nuc900_dma_ops,
 
334
        .pcm_new        = nuc900_dma_new,
 
335
        .pcm_free       = nuc900_dma_free_dma_buffers,
 
336
};
 
337
 
 
338
static int __devinit nuc900_soc_platform_probe(struct platform_device *pdev)
 
339
{
 
340
        return snd_soc_register_platform(&pdev->dev, &nuc900_soc_platform);
 
341
}
 
342
 
 
343
static int __devexit nuc900_soc_platform_remove(struct platform_device *pdev)
 
344
{
 
345
        snd_soc_unregister_platform(&pdev->dev);
 
346
        return 0;
 
347
}
 
348
 
 
349
static struct platform_driver nuc900_pcm_driver = {
 
350
        .driver = {
 
351
                        .name = "nuc900-pcm-audio",
 
352
                        .owner = THIS_MODULE,
 
353
        },
 
354
 
 
355
        .probe = nuc900_soc_platform_probe,
 
356
        .remove = __devexit_p(nuc900_soc_platform_remove),
 
357
};
 
358
 
 
359
static int __init nuc900_pcm_init(void)
 
360
{
 
361
        return platform_driver_register(&nuc900_pcm_driver);
 
362
}
 
363
module_init(nuc900_pcm_init);
 
364
 
 
365
static void __exit nuc900_pcm_exit(void)
 
366
{
 
367
        platform_driver_unregister(&nuc900_pcm_driver);
 
368
}
 
369
module_exit(nuc900_pcm_exit);
 
370
 
 
371
MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
 
372
MODULE_DESCRIPTION("nuc900 Audio DMA module");
 
373
MODULE_LICENSE("GPL");