Version:  2.6.34 2.6.35 2.6.36 2.6.37 2.6.38 2.6.39 3.0 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

Linux/sound/soc/samsung/dma.c

  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 #include <linux/module.h>
 20 
 21 #include <sound/soc.h>
 22 #include <sound/pcm_params.h>
 23 
 24 #include <asm/dma.h>
 25 #include <mach/hardware.h>
 26 #include <mach/dma.h>
 27 
 28 #include "dma.h"
 29 
 30 #define ST_RUNNING              (1<<0)
 31 #define ST_OPENED               (1<<1)
 32 
 33 static const struct snd_pcm_hardware dma_hardware = {
 34         .info                   = SNDRV_PCM_INFO_INTERLEAVED |
 35                                     SNDRV_PCM_INFO_BLOCK_TRANSFER |
 36                                     SNDRV_PCM_INFO_MMAP |
 37                                     SNDRV_PCM_INFO_MMAP_VALID,
 38         .buffer_bytes_max       = 128*1024,
 39         .period_bytes_min       = PAGE_SIZE,
 40         .period_bytes_max       = PAGE_SIZE*2,
 41         .periods_min            = 2,
 42         .periods_max            = 128,
 43         .fifo_size              = 32,
 44 };
 45 
 46 struct runtime_data {
 47         spinlock_t lock;
 48         int state;
 49         unsigned int dma_loaded;
 50         unsigned int dma_period;
 51         dma_addr_t dma_start;
 52         dma_addr_t dma_pos;
 53         dma_addr_t dma_end;
 54         struct s3c_dma_params *params;
 55 };
 56 
 57 static void audio_buffdone(void *data);
 58 
 59 /* dma_enqueue
 60  *
 61  * place a dma buffer onto the queue for the dma system
 62  * to handle.
 63  */
 64 static void dma_enqueue(struct snd_pcm_substream *substream)
 65 {
 66         struct runtime_data *prtd = substream->runtime->private_data;
 67         dma_addr_t pos = prtd->dma_pos;
 68         unsigned int limit;
 69         struct samsung_dma_prep dma_info;
 70 
 71         pr_debug("Entered %s\n", __func__);
 72 
 73         limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
 74 
 75         pr_debug("%s: loaded %d, limit %d\n",
 76                                 __func__, prtd->dma_loaded, limit);
 77 
 78         dma_info.cap = (samsung_dma_has_circular() ? DMA_CYCLIC : DMA_SLAVE);
 79         dma_info.direction =
 80                 (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
 81                 ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM);
 82         dma_info.fp = audio_buffdone;
 83         dma_info.fp_param = substream;
 84         dma_info.period = prtd->dma_period;
 85         dma_info.len = prtd->dma_period*limit;
 86 
 87         if (dma_info.cap == DMA_CYCLIC) {
 88                 dma_info.buf = pos;
 89                 prtd->params->ops->prepare(prtd->params->ch, &dma_info);
 90                 prtd->dma_loaded += limit;
 91                 return;
 92         }
 93 
 94         while (prtd->dma_loaded < limit) {
 95                 pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
 96 
 97                 if ((pos + dma_info.period) > prtd->dma_end) {
 98                         dma_info.period  = prtd->dma_end - pos;
 99                         pr_debug("%s: corrected dma len %ld\n",
100                                         __func__, dma_info.period);
101                 }
102 
103                 dma_info.buf = pos;
104                 prtd->params->ops->prepare(prtd->params->ch, &dma_info);
105 
106                 prtd->dma_loaded++;
107                 pos += prtd->dma_period;
108                 if (pos >= prtd->dma_end)
109                         pos = prtd->dma_start;
110         }
111 
112         prtd->dma_pos = pos;
113 }
114 
115 static void audio_buffdone(void *data)
116 {
117         struct snd_pcm_substream *substream = data;
118         struct runtime_data *prtd = substream->runtime->private_data;
119 
120         pr_debug("Entered %s\n", __func__);
121 
122         if (prtd->state & ST_RUNNING) {
123                 prtd->dma_pos += prtd->dma_period;
124                 if (prtd->dma_pos >= prtd->dma_end)
125                         prtd->dma_pos = prtd->dma_start;
126 
127                 if (substream)
128                         snd_pcm_period_elapsed(substream);
129 
130                 spin_lock(&prtd->lock);
131                 if (!samsung_dma_has_circular()) {
132                         prtd->dma_loaded--;
133                         dma_enqueue(substream);
134                 }
135                 spin_unlock(&prtd->lock);
136         }
137 }
138 
139 static int dma_hw_params(struct snd_pcm_substream *substream,
140         struct snd_pcm_hw_params *params)
141 {
142         struct snd_pcm_runtime *runtime = substream->runtime;
143         struct runtime_data *prtd = runtime->private_data;
144         struct snd_soc_pcm_runtime *rtd = substream->private_data;
145         unsigned long totbytes = params_buffer_bytes(params);
146         struct s3c_dma_params *dma =
147                 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
148         struct samsung_dma_req req;
149         struct samsung_dma_config config;
150 
151         pr_debug("Entered %s\n", __func__);
152 
153         /* return if this is a bufferless transfer e.g.
154          * codec <--> BT codec or GSM modem -- lg FIXME */
155         if (!dma)
156                 return 0;
157 
158         /* this may get called several times by oss emulation
159          * with different params -HW */
160         if (prtd->params == NULL) {
161                 /* prepare DMA */
162                 prtd->params = dma;
163 
164                 pr_debug("params %p, client %p, channel %d\n", prtd->params,
165                         prtd->params->client, prtd->params->channel);
166 
167                 prtd->params->ops = samsung_dma_get_ops();
168 
169                 req.cap = (samsung_dma_has_circular() ?
170                         DMA_CYCLIC : DMA_SLAVE);
171                 req.client = prtd->params->client;
172                 config.direction =
173                         (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
174                         ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM);
175                 config.width = prtd->params->dma_size;
176                 config.fifo = prtd->params->dma_addr;
177                 prtd->params->ch = prtd->params->ops->request(
178                                 prtd->params->channel, &req, rtd->cpu_dai->dev,
179                                 prtd->params->ch_name);
180                 if (!prtd->params->ch) {
181                         pr_err("Failed to allocate DMA channel\n");
182                         return -ENXIO;
183                 }
184                 prtd->params->ops->config(prtd->params->ch, &config);
185         }
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_period = params_period_bytes(params);
194         prtd->dma_start = runtime->dma_addr;
195         prtd->dma_pos = prtd->dma_start;
196         prtd->dma_end = prtd->dma_start + totbytes;
197         spin_unlock_irq(&prtd->lock);
198 
199         return 0;
200 }
201 
202 static int dma_hw_free(struct snd_pcm_substream *substream)
203 {
204         struct runtime_data *prtd = substream->runtime->private_data;
205 
206         pr_debug("Entered %s\n", __func__);
207 
208         snd_pcm_set_runtime_buffer(substream, NULL);
209 
210         if (prtd->params) {
211                 prtd->params->ops->flush(prtd->params->ch);
212                 prtd->params->ops->release(prtd->params->ch,
213                                         prtd->params->client);
214                 prtd->params = NULL;
215         }
216 
217         return 0;
218 }
219 
220 static int dma_prepare(struct snd_pcm_substream *substream)
221 {
222         struct 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         /* flush the DMA channel */
233         prtd->params->ops->flush(prtd->params->ch);
234 
235         prtd->dma_loaded = 0;
236         prtd->dma_pos = prtd->dma_start;
237 
238         /* enqueue dma buffers */
239         dma_enqueue(substream);
240 
241         return ret;
242 }
243 
244 static int dma_trigger(struct snd_pcm_substream *substream, int cmd)
245 {
246         struct runtime_data *prtd = substream->runtime->private_data;
247         int ret = 0;
248 
249         pr_debug("Entered %s\n", __func__);
250 
251         spin_lock(&prtd->lock);
252 
253         switch (cmd) {
254         case SNDRV_PCM_TRIGGER_START:
255                 prtd->state |= ST_RUNNING;
256                 prtd->params->ops->trigger(prtd->params->ch);
257                 break;
258 
259         case SNDRV_PCM_TRIGGER_STOP:
260                 prtd->state &= ~ST_RUNNING;
261                 prtd->params->ops->stop(prtd->params->ch);
262                 break;
263 
264         default:
265                 ret = -EINVAL;
266                 break;
267         }
268 
269         spin_unlock(&prtd->lock);
270 
271         return ret;
272 }
273 
274 static snd_pcm_uframes_t
275 dma_pointer(struct snd_pcm_substream *substream)
276 {
277         struct snd_pcm_runtime *runtime = substream->runtime;
278         struct runtime_data *prtd = runtime->private_data;
279         unsigned long res;
280 
281         pr_debug("Entered %s\n", __func__);
282 
283         res = prtd->dma_pos - prtd->dma_start;
284 
285         pr_debug("Pointer offset: %lu\n", res);
286 
287         /* we seem to be getting the odd error from the pcm library due
288          * to out-of-bounds pointers. this is maybe due to the dma engine
289          * not having loaded the new values for the channel before being
290          * called... (todo - fix )
291          */
292 
293         if (res >= snd_pcm_lib_buffer_bytes(substream)) {
294                 if (res == snd_pcm_lib_buffer_bytes(substream))
295                         res = 0;
296         }
297 
298         return bytes_to_frames(substream->runtime, res);
299 }
300 
301 static int dma_open(struct snd_pcm_substream *substream)
302 {
303         struct snd_pcm_runtime *runtime = substream->runtime;
304         struct runtime_data *prtd;
305 
306         pr_debug("Entered %s\n", __func__);
307 
308         snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
309         snd_soc_set_runtime_hwparams(substream, &dma_hardware);
310 
311         prtd = kzalloc(sizeof(struct runtime_data), GFP_KERNEL);
312         if (prtd == NULL)
313                 return -ENOMEM;
314 
315         spin_lock_init(&prtd->lock);
316 
317         runtime->private_data = prtd;
318         return 0;
319 }
320 
321 static int dma_close(struct snd_pcm_substream *substream)
322 {
323         struct snd_pcm_runtime *runtime = substream->runtime;
324         struct runtime_data *prtd = runtime->private_data;
325 
326         pr_debug("Entered %s\n", __func__);
327 
328         if (!prtd)
329                 pr_debug("dma_close called with prtd == NULL\n");
330 
331         kfree(prtd);
332 
333         return 0;
334 }
335 
336 static int dma_mmap(struct snd_pcm_substream *substream,
337         struct vm_area_struct *vma)
338 {
339         struct snd_pcm_runtime *runtime = substream->runtime;
340 
341         pr_debug("Entered %s\n", __func__);
342 
343         return dma_mmap_writecombine(substream->pcm->card->dev, vma,
344                                      runtime->dma_area,
345                                      runtime->dma_addr,
346                                      runtime->dma_bytes);
347 }
348 
349 static struct snd_pcm_ops dma_ops = {
350         .open           = dma_open,
351         .close          = dma_close,
352         .ioctl          = snd_pcm_lib_ioctl,
353         .hw_params      = dma_hw_params,
354         .hw_free        = dma_hw_free,
355         .prepare        = dma_prepare,
356         .trigger        = dma_trigger,
357         .pointer        = dma_pointer,
358         .mmap           = dma_mmap,
359 };
360 
361 static int preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
362 {
363         struct snd_pcm_substream *substream = pcm->streams[stream].substream;
364         struct snd_dma_buffer *buf = &substream->dma_buffer;
365         size_t size = dma_hardware.buffer_bytes_max;
366 
367         pr_debug("Entered %s\n", __func__);
368 
369         buf->dev.type = SNDRV_DMA_TYPE_DEV;
370         buf->dev.dev = pcm->card->dev;
371         buf->private_data = NULL;
372         buf->area = dma_alloc_writecombine(pcm->card->dev, size,
373                                            &buf->addr, GFP_KERNEL);
374         if (!buf->area)
375                 return -ENOMEM;
376         buf->bytes = size;
377         return 0;
378 }
379 
380 static void dma_free_dma_buffers(struct snd_pcm *pcm)
381 {
382         struct snd_pcm_substream *substream;
383         struct snd_dma_buffer *buf;
384         int stream;
385 
386         pr_debug("Entered %s\n", __func__);
387 
388         for (stream = 0; stream < 2; stream++) {
389                 substream = pcm->streams[stream].substream;
390                 if (!substream)
391                         continue;
392 
393                 buf = &substream->dma_buffer;
394                 if (!buf->area)
395                         continue;
396 
397                 dma_free_writecombine(pcm->card->dev, buf->bytes,
398                                       buf->area, buf->addr);
399                 buf->area = NULL;
400         }
401 }
402 
403 static int dma_new(struct snd_soc_pcm_runtime *rtd)
404 {
405         struct snd_card *card = rtd->card->snd_card;
406         struct snd_pcm *pcm = rtd->pcm;
407         int ret;
408 
409         pr_debug("Entered %s\n", __func__);
410 
411         ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
412         if (ret)
413                 return ret;
414 
415         if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
416                 ret = preallocate_dma_buffer(pcm,
417                         SNDRV_PCM_STREAM_PLAYBACK);
418                 if (ret)
419                         goto out;
420         }
421 
422         if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
423                 ret = preallocate_dma_buffer(pcm,
424                         SNDRV_PCM_STREAM_CAPTURE);
425                 if (ret)
426                         goto out;
427         }
428 out:
429         return ret;
430 }
431 
432 static struct snd_soc_platform_driver samsung_asoc_platform = {
433         .ops            = &dma_ops,
434         .pcm_new        = dma_new,
435         .pcm_free       = dma_free_dma_buffers,
436 };
437 
438 void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
439                                 struct s3c_dma_params *playback,
440                                 struct s3c_dma_params *capture)
441 {
442         snd_soc_dai_init_dma_data(dai, playback, capture);
443 }
444 EXPORT_SYMBOL_GPL(samsung_asoc_init_dma_data);
445 
446 int samsung_asoc_dma_platform_register(struct device *dev)
447 {
448         return snd_soc_register_platform(dev, &samsung_asoc_platform);
449 }
450 EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
451 
452 void samsung_asoc_dma_platform_unregister(struct device *dev)
453 {
454         snd_soc_unregister_platform(dev);
455 }
456 EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_unregister);
457 
458 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
459 MODULE_DESCRIPTION("Samsung ASoC DMA Driver");
460 MODULE_LICENSE("GPL");
461 

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