Merge branch 'modsplit-Oct31_2011' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / 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                                     SNDRV_PCM_INFO_PAUSE |
39                                     SNDRV_PCM_INFO_RESUME,
40         .formats                = SNDRV_PCM_FMTBIT_S16_LE |
41                                     SNDRV_PCM_FMTBIT_U16_LE |
42                                     SNDRV_PCM_FMTBIT_U8 |
43                                     SNDRV_PCM_FMTBIT_S8,
44         .channels_min           = 2,
45         .channels_max           = 2,
46         .buffer_bytes_max       = 128*1024,
47         .period_bytes_min       = PAGE_SIZE,
48         .period_bytes_max       = PAGE_SIZE*2,
49         .periods_min            = 2,
50         .periods_max            = 128,
51         .fifo_size              = 32,
52 };
53
54 struct runtime_data {
55         spinlock_t lock;
56         int state;
57         unsigned int dma_loaded;
58         unsigned int dma_period;
59         dma_addr_t dma_start;
60         dma_addr_t dma_pos;
61         dma_addr_t dma_end;
62         struct s3c_dma_params *params;
63 };
64
65 static void audio_buffdone(void *data);
66
67 /* dma_enqueue
68  *
69  * place a dma buffer onto the queue for the dma system
70  * to handle.
71  */
72 static void dma_enqueue(struct snd_pcm_substream *substream)
73 {
74         struct runtime_data *prtd = substream->runtime->private_data;
75         dma_addr_t pos = prtd->dma_pos;
76         unsigned int limit;
77         struct samsung_dma_prep_info dma_info;
78
79         pr_debug("Entered %s\n", __func__);
80
81         limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
82
83         pr_debug("%s: loaded %d, limit %d\n",
84                                 __func__, prtd->dma_loaded, limit);
85
86         dma_info.cap = (samsung_dma_has_circular() ? DMA_CYCLIC : DMA_SLAVE);
87         dma_info.direction =
88                 (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
89                 ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
90         dma_info.fp = audio_buffdone;
91         dma_info.fp_param = substream;
92         dma_info.period = prtd->dma_period;
93         dma_info.len = prtd->dma_period*limit;
94
95         while (prtd->dma_loaded < limit) {
96                 pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
97
98                 if ((pos + dma_info.period) > prtd->dma_end) {
99                         dma_info.period  = prtd->dma_end - pos;
100                         pr_debug("%s: corrected dma len %ld\n",
101                                         __func__, dma_info.period);
102                 }
103
104                 dma_info.buf = pos;
105                 prtd->params->ops->prepare(prtd->params->ch, &dma_info);
106
107                 prtd->dma_loaded++;
108                 pos += prtd->dma_period;
109                 if (pos >= prtd->dma_end)
110                         pos = prtd->dma_start;
111         }
112
113         prtd->dma_pos = pos;
114 }
115
116 static void audio_buffdone(void *data)
117 {
118         struct snd_pcm_substream *substream = data;
119         struct runtime_data *prtd = substream->runtime->private_data;
120
121         pr_debug("Entered %s\n", __func__);
122
123         if (prtd->state & ST_RUNNING) {
124                 prtd->dma_pos += prtd->dma_period;
125                 if (prtd->dma_pos >= prtd->dma_end)
126                         prtd->dma_pos = prtd->dma_start;
127
128                 if (substream)
129                         snd_pcm_period_elapsed(substream);
130
131                 spin_lock(&prtd->lock);
132                 if (!samsung_dma_has_circular()) {
133                         prtd->dma_loaded--;
134                         dma_enqueue(substream);
135                 }
136                 spin_unlock(&prtd->lock);
137         }
138 }
139
140 static int dma_hw_params(struct snd_pcm_substream *substream,
141         struct snd_pcm_hw_params *params)
142 {
143         struct snd_pcm_runtime *runtime = substream->runtime;
144         struct runtime_data *prtd = runtime->private_data;
145         struct snd_soc_pcm_runtime *rtd = substream->private_data;
146         unsigned long totbytes = params_buffer_bytes(params);
147         struct s3c_dma_params *dma =
148                 snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
149         struct samsung_dma_info dma_info;
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                 dma_info.cap = (samsung_dma_has_circular() ?
170                         DMA_CYCLIC : DMA_SLAVE);
171                 dma_info.client = prtd->params->client;
172                 dma_info.direction =
173                         (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
174                         ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
175                 dma_info.width = prtd->params->dma_size;
176                 dma_info.fifo = prtd->params->dma_addr;
177                 prtd->params->ch = prtd->params->ops->request(
178                                 prtd->params->channel, &dma_info);
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) {
248         case SNDRV_PCM_TRIGGER_START:
249         case SNDRV_PCM_TRIGGER_RESUME:
250         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
251                 prtd->state |= ST_RUNNING;
252                 prtd->params->ops->trigger(prtd->params->ch);
253                 break;
254
255         case SNDRV_PCM_TRIGGER_STOP:
256         case SNDRV_PCM_TRIGGER_SUSPEND:
257         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
258                 prtd->state &= ~ST_RUNNING;
259                 prtd->params->ops->stop(prtd->params->ch);
260                 break;
261
262         default:
263                 ret = -EINVAL;
264                 break;
265         }
266
267         spin_unlock(&prtd->lock);
268
269         return ret;
270 }
271
272 static snd_pcm_uframes_t
273 dma_pointer(struct snd_pcm_substream *substream)
274 {
275         struct snd_pcm_runtime *runtime = substream->runtime;
276         struct runtime_data *prtd = runtime->private_data;
277         unsigned long res;
278
279         pr_debug("Entered %s\n", __func__);
280
281         res = prtd->dma_pos - prtd->dma_start;
282
283         pr_debug("Pointer offset: %lu\n", res);
284
285         /* we seem to be getting the odd error from the pcm library due
286          * to out-of-bounds pointers. this is maybe due to the dma engine
287          * not having loaded the new values for the channel before being
288          * called... (todo - fix )
289          */
290
291         if (res >= snd_pcm_lib_buffer_bytes(substream)) {
292                 if (res == snd_pcm_lib_buffer_bytes(substream))
293                         res = 0;
294         }
295
296         return bytes_to_frames(substream->runtime, res);
297 }
298
299 static int dma_open(struct snd_pcm_substream *substream)
300 {
301         struct snd_pcm_runtime *runtime = substream->runtime;
302         struct runtime_data *prtd;
303
304         pr_debug("Entered %s\n", __func__);
305
306         snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
307         snd_soc_set_runtime_hwparams(substream, &dma_hardware);
308
309         prtd = kzalloc(sizeof(struct runtime_data), GFP_KERNEL);
310         if (prtd == NULL)
311                 return -ENOMEM;
312
313         spin_lock_init(&prtd->lock);
314
315         runtime->private_data = prtd;
316         return 0;
317 }
318
319 static int dma_close(struct snd_pcm_substream *substream)
320 {
321         struct snd_pcm_runtime *runtime = substream->runtime;
322         struct runtime_data *prtd = runtime->private_data;
323
324         pr_debug("Entered %s\n", __func__);
325
326         if (!prtd)
327                 pr_debug("dma_close called with prtd == NULL\n");
328
329         kfree(prtd);
330
331         return 0;
332 }
333
334 static int dma_mmap(struct snd_pcm_substream *substream,
335         struct vm_area_struct *vma)
336 {
337         struct snd_pcm_runtime *runtime = substream->runtime;
338
339         pr_debug("Entered %s\n", __func__);
340
341         return dma_mmap_writecombine(substream->pcm->card->dev, vma,
342                                      runtime->dma_area,
343                                      runtime->dma_addr,
344                                      runtime->dma_bytes);
345 }
346
347 static struct snd_pcm_ops dma_ops = {
348         .open           = dma_open,
349         .close          = dma_close,
350         .ioctl          = snd_pcm_lib_ioctl,
351         .hw_params      = dma_hw_params,
352         .hw_free        = dma_hw_free,
353         .prepare        = dma_prepare,
354         .trigger        = dma_trigger,
355         .pointer        = dma_pointer,
356         .mmap           = dma_mmap,
357 };
358
359 static int preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
360 {
361         struct snd_pcm_substream *substream = pcm->streams[stream].substream;
362         struct snd_dma_buffer *buf = &substream->dma_buffer;
363         size_t size = dma_hardware.buffer_bytes_max;
364
365         pr_debug("Entered %s\n", __func__);
366
367         buf->dev.type = SNDRV_DMA_TYPE_DEV;
368         buf->dev.dev = pcm->card->dev;
369         buf->private_data = NULL;
370         buf->area = dma_alloc_writecombine(pcm->card->dev, size,
371                                            &buf->addr, GFP_KERNEL);
372         if (!buf->area)
373                 return -ENOMEM;
374         buf->bytes = size;
375         return 0;
376 }
377
378 static void dma_free_dma_buffers(struct snd_pcm *pcm)
379 {
380         struct snd_pcm_substream *substream;
381         struct snd_dma_buffer *buf;
382         int stream;
383
384         pr_debug("Entered %s\n", __func__);
385
386         for (stream = 0; stream < 2; stream++) {
387                 substream = pcm->streams[stream].substream;
388                 if (!substream)
389                         continue;
390
391                 buf = &substream->dma_buffer;
392                 if (!buf->area)
393                         continue;
394
395                 dma_free_writecombine(pcm->card->dev, buf->bytes,
396                                       buf->area, buf->addr);
397                 buf->area = NULL;
398         }
399 }
400
401 static u64 dma_mask = DMA_BIT_MASK(32);
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_soc_dai *dai = rtd->cpu_dai;
407         struct snd_pcm *pcm = rtd->pcm;
408         int ret = 0;
409
410         pr_debug("Entered %s\n", __func__);
411
412         if (!card->dev->dma_mask)
413                 card->dev->dma_mask = &dma_mask;
414         if (!card->dev->coherent_dma_mask)
415                 card->dev->coherent_dma_mask = 0xffffffff;
416
417         if (dai->driver->playback.channels_min) {
418                 ret = preallocate_dma_buffer(pcm,
419                         SNDRV_PCM_STREAM_PLAYBACK);
420                 if (ret)
421                         goto out;
422         }
423
424         if (dai->driver->capture.channels_min) {
425                 ret = preallocate_dma_buffer(pcm,
426                         SNDRV_PCM_STREAM_CAPTURE);
427                 if (ret)
428                         goto out;
429         }
430 out:
431         return ret;
432 }
433
434 static struct snd_soc_platform_driver samsung_asoc_platform = {
435         .ops            = &dma_ops,
436         .pcm_new        = dma_new,
437         .pcm_free       = dma_free_dma_buffers,
438 };
439
440 static int __devinit samsung_asoc_platform_probe(struct platform_device *pdev)
441 {
442         return snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);
443 }
444
445 static int __devexit samsung_asoc_platform_remove(struct platform_device *pdev)
446 {
447         snd_soc_unregister_platform(&pdev->dev);
448         return 0;
449 }
450
451 static struct platform_driver asoc_dma_driver = {
452         .driver = {
453                 .name = "samsung-audio",
454                 .owner = THIS_MODULE,
455         },
456
457         .probe = samsung_asoc_platform_probe,
458         .remove = __devexit_p(samsung_asoc_platform_remove),
459 };
460
461 static int __init samsung_asoc_init(void)
462 {
463         return platform_driver_register(&asoc_dma_driver);
464 }
465 module_init(samsung_asoc_init);
466
467 static void __exit samsung_asoc_exit(void)
468 {
469         platform_driver_unregister(&asoc_dma_driver);
470 }
471 module_exit(samsung_asoc_exit);
472
473 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
474 MODULE_DESCRIPTION("Samsung ASoC DMA Driver");
475 MODULE_LICENSE("GPL");
476 MODULE_ALIAS("platform:samsung-audio");