Version:  2.0.40 2.2.26 2.4.37 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17

Linux/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 void nuc900_dma_free_dma_buffers(struct snd_pcm *pcm)
310 {
311         snd_pcm_lib_preallocate_free_for_all(pcm);
312 }
313 
314 static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd)
315 {
316         struct snd_card *card = rtd->card->snd_card;
317         struct snd_pcm *pcm = rtd->pcm;
318         int ret;
319 
320         ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
321         if (ret)
322                 return ret;
323 
324         snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
325                 card->dev, 4 * 1024, (4 * 1024) - 1);
326 
327         return 0;
328 }
329 
330 static struct snd_soc_platform_driver nuc900_soc_platform = {
331         .ops            = &nuc900_dma_ops,
332         .pcm_new        = nuc900_dma_new,
333         .pcm_free       = nuc900_dma_free_dma_buffers,
334 };
335 
336 static int nuc900_soc_platform_probe(struct platform_device *pdev)
337 {
338         return snd_soc_register_platform(&pdev->dev, &nuc900_soc_platform);
339 }
340 
341 static int nuc900_soc_platform_remove(struct platform_device *pdev)
342 {
343         snd_soc_unregister_platform(&pdev->dev);
344         return 0;
345 }
346 
347 static struct platform_driver nuc900_pcm_driver = {
348         .driver = {
349                         .name = "nuc900-pcm-audio",
350                         .owner = THIS_MODULE,
351         },
352 
353         .probe = nuc900_soc_platform_probe,
354         .remove = nuc900_soc_platform_remove,
355 };
356 
357 module_platform_driver(nuc900_pcm_driver);
358 
359 MODULE_AUTHOR("Wan ZongShun, <mcuos.com@gmail.com>");
360 MODULE_DESCRIPTION("nuc900 Audio DMA module");
361 MODULE_LICENSE("GPL");
362 

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us