]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - sound/soc/nuc900/nuc900-pcm.c
Merge remote-tracking branches 'asoc/topic/rx51', 'asoc/topic/samsung', 'asoc/topic...
[karo-tx-linux.git] / sound / soc / nuc900 / nuc900-pcm.c
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         .buffer_bytes_max       = 4*1024,
36         .period_bytes_min       = 1*1024,
37         .period_bytes_max       = 4*1024,
38         .periods_min            = 1,
39         .periods_max            = 1024,
40 };
41
42 static int nuc900_dma_hw_params(struct snd_pcm_substream *substream,
43         struct snd_pcm_hw_params *params)
44 {
45         struct snd_pcm_runtime *runtime = substream->runtime;
46         struct nuc900_audio *nuc900_audio = runtime->private_data;
47         unsigned long flags;
48         int ret = 0;
49
50         ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
51         if (ret < 0)
52                 return ret;
53
54         spin_lock_irqsave(&nuc900_audio->lock, flags);
55
56         nuc900_audio->substream = substream;
57         nuc900_audio->dma_addr[substream->stream] = runtime->dma_addr;
58         nuc900_audio->buffersize[substream->stream] =
59                                                 params_buffer_bytes(params);
60
61         spin_unlock_irqrestore(&nuc900_audio->lock, flags);
62
63         return ret;
64 }
65
66 static void nuc900_update_dma_register(struct snd_pcm_substream *substream,
67                                 dma_addr_t dma_addr, size_t count)
68 {
69         struct snd_pcm_runtime *runtime = substream->runtime;
70         struct nuc900_audio *nuc900_audio = runtime->private_data;
71         void __iomem *mmio_addr, *mmio_len;
72
73         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
74                 mmio_addr = nuc900_audio->mmio + ACTL_PDSTB;
75                 mmio_len = nuc900_audio->mmio + ACTL_PDST_LENGTH;
76         } else {
77                 mmio_addr = nuc900_audio->mmio + ACTL_RDSTB;
78                 mmio_len = nuc900_audio->mmio + ACTL_RDST_LENGTH;
79         }
80
81         AUDIO_WRITE(mmio_addr, dma_addr);
82         AUDIO_WRITE(mmio_len, count);
83 }
84
85 static void nuc900_dma_start(struct snd_pcm_substream *substream)
86 {
87         struct snd_pcm_runtime *runtime = substream->runtime;
88         struct nuc900_audio *nuc900_audio = runtime->private_data;
89         unsigned long val;
90
91         val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
92         val |= (T_DMA_IRQ | R_DMA_IRQ);
93         AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
94 }
95
96 static void nuc900_dma_stop(struct snd_pcm_substream *substream)
97 {
98         struct snd_pcm_runtime *runtime = substream->runtime;
99         struct nuc900_audio *nuc900_audio = runtime->private_data;
100         unsigned long val;
101
102         val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
103         val &= ~(T_DMA_IRQ | R_DMA_IRQ);
104         AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val);
105 }
106
107 static irqreturn_t nuc900_dma_interrupt(int irq, void *dev_id)
108 {
109         struct snd_pcm_substream *substream = dev_id;
110         struct nuc900_audio *nuc900_audio = substream->runtime->private_data;
111         unsigned long val;
112
113         spin_lock(&nuc900_audio->lock);
114
115         val = AUDIO_READ(nuc900_audio->mmio + ACTL_CON);
116
117         if (val & R_DMA_IRQ) {
118                 AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | R_DMA_IRQ);
119
120                 val = AUDIO_READ(nuc900_audio->mmio + ACTL_RSR);
121
122                 if (val & R_DMA_MIDDLE_IRQ) {
123                         val |= R_DMA_MIDDLE_IRQ;
124                         AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
125                 }
126
127                 if (val & R_DMA_END_IRQ) {
128                         val |= R_DMA_END_IRQ;
129                         AUDIO_WRITE(nuc900_audio->mmio + ACTL_RSR, val);
130                 }
131         } else if (val & T_DMA_IRQ) {
132                 AUDIO_WRITE(nuc900_audio->mmio + ACTL_CON, val | T_DMA_IRQ);
133
134                 val = AUDIO_READ(nuc900_audio->mmio + ACTL_PSR);
135
136                 if (val & P_DMA_MIDDLE_IRQ) {
137                         val |= P_DMA_MIDDLE_IRQ;
138                         AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
139                 }
140
141                 if (val & P_DMA_END_IRQ) {
142                         val |= P_DMA_END_IRQ;
143                         AUDIO_WRITE(nuc900_audio->mmio + ACTL_PSR, val);
144                 }
145         } else {
146                 dev_err(nuc900_audio->dev, "Wrong DMA interrupt status!\n");
147                 spin_unlock(&nuc900_audio->lock);
148                 return IRQ_HANDLED;
149         }
150
151         spin_unlock(&nuc900_audio->lock);
152
153         snd_pcm_period_elapsed(substream);
154
155         return IRQ_HANDLED;
156 }
157
158 static int nuc900_dma_hw_free(struct snd_pcm_substream *substream)
159 {
160         snd_pcm_lib_free_pages(substream);
161         return 0;
162 }
163
164 static int nuc900_dma_prepare(struct snd_pcm_substream *substream)
165 {
166         struct snd_pcm_runtime *runtime = substream->runtime;
167         struct nuc900_audio *nuc900_audio = runtime->private_data;
168         unsigned long flags, val;
169         int ret = 0;
170
171         spin_lock_irqsave(&nuc900_audio->lock, flags);
172
173         nuc900_update_dma_register(substream,
174                                 nuc900_audio->dma_addr[substream->stream],
175                                 nuc900_audio->buffersize[substream->stream]);
176
177         val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET);
178
179         switch (runtime->channels) {
180         case 1:
181                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
182                         val &= ~(PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
183                         val |= PLAY_RIGHT_CHNNEL;
184                 } else {
185                         val &= ~(RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
186                         val |= RECORD_RIGHT_CHNNEL;
187                 }
188                 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
189                 break;
190         case 2:
191                 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
192                         val |= (PLAY_LEFT_CHNNEL | PLAY_RIGHT_CHNNEL);
193                 else
194                         val |= (RECORD_LEFT_CHNNEL | RECORD_RIGHT_CHNNEL);
195                 AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val);
196                 break;
197         default:
198                 ret = -EINVAL;
199         }
200         spin_unlock_irqrestore(&nuc900_audio->lock, flags);
201         return ret;
202 }
203
204 static int nuc900_dma_trigger(struct snd_pcm_substream *substream, int cmd)
205 {
206         int ret = 0;
207
208         switch (cmd) {
209         case SNDRV_PCM_TRIGGER_START:
210         case SNDRV_PCM_TRIGGER_RESUME:
211                 nuc900_dma_start(substream);
212                 break;
213
214         case SNDRV_PCM_TRIGGER_STOP:
215         case SNDRV_PCM_TRIGGER_SUSPEND:
216                 nuc900_dma_stop(substream);
217                 break;
218
219         default:
220                 ret = -EINVAL;
221                 break;
222         }
223
224         return ret;
225 }
226
227 static int nuc900_dma_getposition(struct snd_pcm_substream *substream,
228                                         dma_addr_t *src, dma_addr_t *dst)
229 {
230         struct snd_pcm_runtime *runtime = substream->runtime;
231         struct nuc900_audio *nuc900_audio = runtime->private_data;
232
233         if (src != NULL)
234                 *src = AUDIO_READ(nuc900_audio->mmio + ACTL_PDSTC);
235
236         if (dst != NULL)
237                 *dst = AUDIO_READ(nuc900_audio->mmio + ACTL_RDSTC);
238
239         return 0;
240 }
241
242 static snd_pcm_uframes_t nuc900_dma_pointer(struct snd_pcm_substream *substream)
243 {
244         struct snd_pcm_runtime *runtime = substream->runtime;
245         dma_addr_t src, dst;
246         unsigned long res;
247
248         nuc900_dma_getposition(substream, &src, &dst);
249
250         if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
251                 res = dst - runtime->dma_addr;
252         else
253                 res = src - runtime->dma_addr;
254
255         return bytes_to_frames(substream->runtime, res);
256 }
257
258 static int nuc900_dma_open(struct snd_pcm_substream *substream)
259 {
260         struct snd_pcm_runtime *runtime = substream->runtime;
261         struct nuc900_audio *nuc900_audio;
262
263         snd_soc_set_runtime_hwparams(substream, &nuc900_pcm_hardware);
264
265         nuc900_audio = nuc900_ac97_data;
266
267         if (request_irq(nuc900_audio->irq_num, nuc900_dma_interrupt,
268                         0, "nuc900-dma", substream))
269                 return -EBUSY;
270
271         runtime->private_data = nuc900_audio;
272
273         return 0;
274 }
275
276 static int nuc900_dma_close(struct snd_pcm_substream *substream)
277 {
278         struct snd_pcm_runtime *runtime = substream->runtime;
279         struct nuc900_audio *nuc900_audio = runtime->private_data;
280
281         free_irq(nuc900_audio->irq_num, substream);
282
283         return 0;
284 }
285
286 static int nuc900_dma_mmap(struct snd_pcm_substream *substream,
287         struct vm_area_struct *vma)
288 {
289         struct snd_pcm_runtime *runtime = substream->runtime;
290
291         return dma_mmap_writecombine(substream->pcm->card->dev, vma,
292                                         runtime->dma_area,
293                                         runtime->dma_addr,
294                                         runtime->dma_bytes);
295 }
296
297 static struct snd_pcm_ops nuc900_dma_ops = {
298         .open           = nuc900_dma_open,
299         .close          = nuc900_dma_close,
300         .ioctl          = snd_pcm_lib_ioctl,
301         .hw_params      = nuc900_dma_hw_params,
302         .hw_free        = nuc900_dma_hw_free,
303         .prepare        = nuc900_dma_prepare,
304         .trigger        = nuc900_dma_trigger,
305         .pointer        = nuc900_dma_pointer,
306         .mmap           = nuc900_dma_mmap,
307 };
308
309 static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd)
310 {
311         struct snd_card *card = rtd->card->snd_card;
312         struct snd_pcm *pcm = rtd->pcm;
313         int ret;
314
315         ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
316         if (ret)
317                 return ret;
318
319         snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
320                 card->dev, 4 * 1024, (4 * 1024) - 1);
321
322         return 0;
323 }
324
325 static struct snd_soc_platform_driver nuc900_soc_platform = {
326         .ops            = &nuc900_dma_ops,
327         .pcm_new        = nuc900_dma_new,
328 };
329
330 static int nuc900_soc_platform_probe(struct platform_device *pdev)
331 {
332         return snd_soc_register_platform(&pdev->dev, &nuc900_soc_platform);
333 }
334
335 static int nuc900_soc_platform_remove(struct platform_device *pdev)
336 {
337         snd_soc_unregister_platform(&pdev->dev);
338         return 0;
339 }
340
341 static struct platform_driver nuc900_pcm_driver = {
342         .driver = {
343                         .name = "nuc900-pcm-audio",
344         },
345
346         .probe = nuc900_soc_platform_probe,
347         .remove = nuc900_soc_platform_remove,
348 };
349
350 module_platform_driver(nuc900_pcm_driver);
351
352 MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
353 MODULE_DESCRIPTION("nuc900 Audio DMA module");
354 MODULE_LICENSE("GPL");