Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dma.c
Go to the documentation of this file.
1 /*
2  * dma.c -- ALSA Soc Audio Layer
3  *
4  * (c) 2006 Wolfson Microelectronics PLC.
5  * Graeme Gregory [email protected] or [email protected]
6  *
7  * Copyright 2004-2005 Simtec Electronics
8  * http://armlinux.simtec.co.uk/
9  * Ben Dooks <[email protected]>
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 = {
38  .formats = SNDRV_PCM_FMTBIT_S16_LE |
42  .channels_min = 2,
43  .channels_max = 2,
44  .buffer_bytes_max = 128*1024,
45  .period_bytes_min = PAGE_SIZE,
46  .period_bytes_max = PAGE_SIZE*2,
47  .periods_min = 2,
48  .periods_max = 128,
49  .fifo_size = 32,
50 };
51 
52 struct runtime_data {
54  int state;
55  unsigned int dma_loaded;
56  unsigned int dma_period;
61 };
62 
63 static void audio_buffdone(void *data);
64 
65 /* dma_enqueue
66  *
67  * place a dma buffer onto the queue for the dma system
68  * to handle.
69  */
70 static void dma_enqueue(struct snd_pcm_substream *substream)
71 {
72  struct runtime_data *prtd = substream->runtime->private_data;
73  dma_addr_t pos = prtd->dma_pos;
74  unsigned int limit;
76 
77  pr_debug("Entered %s\n", __func__);
78 
79  limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
80 
81  pr_debug("%s: loaded %d, limit %d\n",
82  __func__, prtd->dma_loaded, limit);
83 
84  dma_info.cap = (samsung_dma_has_circular() ? DMA_CYCLIC : DMA_SLAVE);
85  dma_info.direction =
86  (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
88  dma_info.fp = audio_buffdone;
89  dma_info.fp_param = substream;
90  dma_info.period = prtd->dma_period;
91  dma_info.len = prtd->dma_period*limit;
92 
93  while (prtd->dma_loaded < limit) {
94  pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
95 
96  if ((pos + dma_info.period) > prtd->dma_end) {
97  dma_info.period = prtd->dma_end - pos;
98  pr_debug("%s: corrected dma len %ld\n",
99  __func__, dma_info.period);
100  }
101 
102  dma_info.buf = pos;
103  prtd->params->ops->prepare(prtd->params->ch, &dma_info);
104 
105  prtd->dma_loaded++;
106  pos += prtd->dma_period;
107  if (pos >= prtd->dma_end)
108  pos = prtd->dma_start;
109  }
110 
111  prtd->dma_pos = pos;
112 }
113 
114 static void audio_buffdone(void *data)
115 {
116  struct snd_pcm_substream *substream = data;
117  struct runtime_data *prtd = substream->runtime->private_data;
118 
119  pr_debug("Entered %s\n", __func__);
120 
121  if (prtd->state & ST_RUNNING) {
122  prtd->dma_pos += prtd->dma_period;
123  if (prtd->dma_pos >= prtd->dma_end)
124  prtd->dma_pos = prtd->dma_start;
125 
126  if (substream)
127  snd_pcm_period_elapsed(substream);
128 
129  spin_lock(&prtd->lock);
130  if (!samsung_dma_has_circular()) {
131  prtd->dma_loaded--;
132  dma_enqueue(substream);
133  }
134  spin_unlock(&prtd->lock);
135  }
136 }
137 
138 static int dma_hw_params(struct snd_pcm_substream *substream,
139  struct snd_pcm_hw_params *params)
140 {
141  struct snd_pcm_runtime *runtime = substream->runtime;
142  struct runtime_data *prtd = runtime->private_data;
143  struct snd_soc_pcm_runtime *rtd = substream->private_data;
144  unsigned long totbytes = params_buffer_bytes(params);
145  struct s3c_dma_params *dma =
146  snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
147  struct samsung_dma_req req;
148  struct samsung_dma_config config;
149 
150  pr_debug("Entered %s\n", __func__);
151 
152  /* return if this is a bufferless transfer e.g.
153  * codec <--> BT codec or GSM modem -- lg FIXME */
154  if (!dma)
155  return 0;
156 
157  /* this may get called several times by oss emulation
158  * with different params -HW */
159  if (prtd->params == NULL) {
160  /* prepare DMA */
161  prtd->params = dma;
162 
163  pr_debug("params %p, client %p, channel %d\n", prtd->params,
164  prtd->params->client, prtd->params->channel);
165 
166  prtd->params->ops = samsung_dma_get_ops();
167 
168  req.cap = (samsung_dma_has_circular() ?
170  req.client = prtd->params->client;
171  config.direction =
172  (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
174  config.width = prtd->params->dma_size;
175  config.fifo = prtd->params->dma_addr;
176  prtd->params->ch = prtd->params->ops->request(
177  prtd->params->channel, &req);
178  prtd->params->ops->config(prtd->params->ch, &config);
179  }
180 
181  snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
182 
183  runtime->dma_bytes = totbytes;
184 
185  spin_lock_irq(&prtd->lock);
186  prtd->dma_loaded = 0;
187  prtd->dma_period = params_period_bytes(params);
188  prtd->dma_start = runtime->dma_addr;
189  prtd->dma_pos = prtd->dma_start;
190  prtd->dma_end = prtd->dma_start + totbytes;
191  spin_unlock_irq(&prtd->lock);
192 
193  return 0;
194 }
195 
196 static int dma_hw_free(struct snd_pcm_substream *substream)
197 {
198  struct runtime_data *prtd = substream->runtime->private_data;
199 
200  pr_debug("Entered %s\n", __func__);
201 
202  snd_pcm_set_runtime_buffer(substream, NULL);
203 
204  if (prtd->params) {
205  prtd->params->ops->flush(prtd->params->ch);
206  prtd->params->ops->release(prtd->params->ch,
207  prtd->params->client);
208  prtd->params = NULL;
209  }
210 
211  return 0;
212 }
213 
214 static int dma_prepare(struct snd_pcm_substream *substream)
215 {
216  struct runtime_data *prtd = substream->runtime->private_data;
217  int ret = 0;
218 
219  pr_debug("Entered %s\n", __func__);
220 
221  /* return if this is a bufferless transfer e.g.
222  * codec <--> BT codec or GSM modem -- lg FIXME */
223  if (!prtd->params)
224  return 0;
225 
226  /* flush the DMA channel */
227  prtd->params->ops->flush(prtd->params->ch);
228 
229  prtd->dma_loaded = 0;
230  prtd->dma_pos = prtd->dma_start;
231 
232  /* enqueue dma buffers */
233  dma_enqueue(substream);
234 
235  return ret;
236 }
237 
238 static int dma_trigger(struct snd_pcm_substream *substream, int cmd)
239 {
240  struct runtime_data *prtd = substream->runtime->private_data;
241  int ret = 0;
242 
243  pr_debug("Entered %s\n", __func__);
244 
245  spin_lock(&prtd->lock);
246 
247  switch (cmd) {
249  prtd->state |= ST_RUNNING;
250  prtd->params->ops->trigger(prtd->params->ch);
251  break;
252 
254  prtd->state &= ~ST_RUNNING;
255  prtd->params->ops->stop(prtd->params->ch);
256  break;
257 
258  default:
259  ret = -EINVAL;
260  break;
261  }
262 
263  spin_unlock(&prtd->lock);
264 
265  return ret;
266 }
267 
268 static snd_pcm_uframes_t
269 dma_pointer(struct snd_pcm_substream *substream)
270 {
271  struct snd_pcm_runtime *runtime = substream->runtime;
272  struct runtime_data *prtd = runtime->private_data;
273  unsigned long res;
274 
275  pr_debug("Entered %s\n", __func__);
276 
277  res = prtd->dma_pos - prtd->dma_start;
278 
279  pr_debug("Pointer offset: %lu\n", res);
280 
281  /* we seem to be getting the odd error from the pcm library due
282  * to out-of-bounds pointers. this is maybe due to the dma engine
283  * not having loaded the new values for the channel before being
284  * called... (todo - fix )
285  */
286 
287  if (res >= snd_pcm_lib_buffer_bytes(substream)) {
288  if (res == snd_pcm_lib_buffer_bytes(substream))
289  res = 0;
290  }
291 
292  return bytes_to_frames(substream->runtime, res);
293 }
294 
295 static int dma_open(struct snd_pcm_substream *substream)
296 {
297  struct snd_pcm_runtime *runtime = substream->runtime;
298  struct runtime_data *prtd;
299 
300  pr_debug("Entered %s\n", __func__);
301 
303  snd_soc_set_runtime_hwparams(substream, &dma_hardware);
304 
305  prtd = kzalloc(sizeof(struct runtime_data), GFP_KERNEL);
306  if (prtd == NULL)
307  return -ENOMEM;
308 
309  spin_lock_init(&prtd->lock);
310 
311  runtime->private_data = prtd;
312  return 0;
313 }
314 
315 static int dma_close(struct snd_pcm_substream *substream)
316 {
317  struct snd_pcm_runtime *runtime = substream->runtime;
318  struct runtime_data *prtd = runtime->private_data;
319 
320  pr_debug("Entered %s\n", __func__);
321 
322  if (!prtd)
323  pr_debug("dma_close called with prtd == NULL\n");
324 
325  kfree(prtd);
326 
327  return 0;
328 }
329 
330 static int dma_mmap(struct snd_pcm_substream *substream,
331  struct vm_area_struct *vma)
332 {
333  struct snd_pcm_runtime *runtime = substream->runtime;
334 
335  pr_debug("Entered %s\n", __func__);
336 
337  return dma_mmap_writecombine(substream->pcm->card->dev, vma,
338  runtime->dma_area,
339  runtime->dma_addr,
340  runtime->dma_bytes);
341 }
342 
343 static struct snd_pcm_ops dma_ops = {
344  .open = dma_open,
345  .close = dma_close,
346  .ioctl = snd_pcm_lib_ioctl,
347  .hw_params = dma_hw_params,
348  .hw_free = dma_hw_free,
349  .prepare = dma_prepare,
350  .trigger = dma_trigger,
351  .pointer = dma_pointer,
352  .mmap = dma_mmap,
353 };
354 
355 static int preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
356 {
357  struct snd_pcm_substream *substream = pcm->streams[stream].substream;
358  struct snd_dma_buffer *buf = &substream->dma_buffer;
359  size_t size = dma_hardware.buffer_bytes_max;
360 
361  pr_debug("Entered %s\n", __func__);
362 
363  buf->dev.type = SNDRV_DMA_TYPE_DEV;
364  buf->dev.dev = pcm->card->dev;
365  buf->private_data = NULL;
366  buf->area = dma_alloc_writecombine(pcm->card->dev, size,
367  &buf->addr, GFP_KERNEL);
368  if (!buf->area)
369  return -ENOMEM;
370  buf->bytes = size;
371  return 0;
372 }
373 
374 static void dma_free_dma_buffers(struct snd_pcm *pcm)
375 {
376  struct snd_pcm_substream *substream;
377  struct snd_dma_buffer *buf;
378  int stream;
379 
380  pr_debug("Entered %s\n", __func__);
381 
382  for (stream = 0; stream < 2; stream++) {
383  substream = pcm->streams[stream].substream;
384  if (!substream)
385  continue;
386 
387  buf = &substream->dma_buffer;
388  if (!buf->area)
389  continue;
390 
391  dma_free_writecombine(pcm->card->dev, buf->bytes,
392  buf->area, buf->addr);
393  buf->area = NULL;
394  }
395 }
396 
397 static u64 dma_mask = DMA_BIT_MASK(32);
398 
399 static int dma_new(struct snd_soc_pcm_runtime *rtd)
400 {
401  struct snd_card *card = rtd->card->snd_card;
402  struct snd_pcm *pcm = rtd->pcm;
403  int ret = 0;
404 
405  pr_debug("Entered %s\n", __func__);
406 
407  if (!card->dev->dma_mask)
408  card->dev->dma_mask = &dma_mask;
409  if (!card->dev->coherent_dma_mask)
410  card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
411 
412  if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
413  ret = preallocate_dma_buffer(pcm,
415  if (ret)
416  goto out;
417  }
418 
419  if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
420  ret = preallocate_dma_buffer(pcm,
422  if (ret)
423  goto out;
424  }
425 out:
426  return ret;
427 }
428 
429 static struct snd_soc_platform_driver samsung_asoc_platform = {
430  .ops = &dma_ops,
431  .pcm_new = dma_new,
432  .pcm_free = dma_free_dma_buffers,
433 };
434 
435 static int __devinit samsung_asoc_platform_probe(struct platform_device *pdev)
436 {
437  return snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);
438 }
439 
440 static int __devexit samsung_asoc_platform_remove(struct platform_device *pdev)
441 {
443  return 0;
444 }
445 
446 static struct platform_driver asoc_dma_driver = {
447  .driver = {
448  .name = "samsung-audio",
449  .owner = THIS_MODULE,
450  },
451 
452  .probe = samsung_asoc_platform_probe,
453  .remove = __devexit_p(samsung_asoc_platform_remove),
454 };
455 
456 module_platform_driver(asoc_dma_driver);
457 
458 MODULE_AUTHOR("Ben Dooks, <[email protected]>");
459 MODULE_DESCRIPTION("Samsung ASoC DMA Driver");
460 MODULE_LICENSE("GPL");
461 MODULE_ALIAS("platform:samsung-audio");