Merge tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck...
[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 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_MEM_TO_DEV : DMA_DEV_TO_MEM);
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_req req;
150         struct samsung_dma_config config;
151
152         pr_debug("Entered %s\n", __func__);
153
154         /* return if this is a bufferless transfer e.g.
155          * codec <--> BT codec or GSM modem -- lg FIXME */
156         if (!dma)
157                 return 0;
158
159         /* this may get called several times by oss emulation
160          * with different params -HW */
161         if (prtd->params == NULL) {
162                 /* prepare DMA */
163                 prtd->params = dma;
164
165                 pr_debug("params %p, client %p, channel %d\n", prtd->params,
166                         prtd->params->client, prtd->params->channel);
167
168                 prtd->params->ops = samsung_dma_get_ops();
169
170                 req.cap = (samsung_dma_has_circular() ?
171                         DMA_CYCLIC : DMA_SLAVE);
172                 req.client = prtd->params->client;
173                 config.direction =
174                         (substream->stream == SNDRV_PCM_STREAM_PLAYBACK
175                         ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM);
176                 config.width = prtd->params->dma_size;
177                 config.fifo = prtd->params->dma_addr;
178                 prtd->params->ch = prtd->params->ops->request(
179                                 prtd->params->channel, &req);
180                 prtd->params->ops->config(prtd->params->ch, &config);
181         }
182
183         snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
184
185         runtime->dma_bytes = totbytes;
186
187         spin_lock_irq(&prtd->lock);
188         prtd->dma_loaded = 0;
189         prtd->dma_period = params_period_bytes(params);
190         prtd->dma_start = runtime->dma_addr;
191         prtd->dma_pos = prtd->dma_start;
192         prtd->dma_end = prtd->dma_start + totbytes;
193         spin_unlock_irq(&prtd->lock);
194
195         return 0;
196 }
197
198 static int dma_hw_free(struct snd_pcm_substream *substream)
199 {
200         struct runtime_data *prtd = substream->runtime->private_data;
201
202         pr_debug("Entered %s\n", __func__);
203
204         snd_pcm_set_runtime_buffer(substream, NULL);
205
206         if (prtd->params) {
207                 prtd->params->ops->flush(prtd->params->ch);
208                 prtd->params->ops->release(prtd->params->ch,
209                                         prtd->params->client);
210                 prtd->params = NULL;
211         }
212
213         return 0;
214 }
215
216 static int dma_prepare(struct snd_pcm_substream *substream)
217 {
218         struct runtime_data *prtd = substream->runtime->private_data;
219         int ret = 0;
220
221         pr_debug("Entered %s\n", __func__);
222
223         /* return if this is a bufferless transfer e.g.
224          * codec <--> BT codec or GSM modem -- lg FIXME */
225         if (!prtd->params)
226                 return 0;
227
228         /* flush the DMA channel */
229         prtd->params->ops->flush(prtd->params->ch);
230
231         prtd->dma_loaded = 0;
232         prtd->dma_pos = prtd->dma_start;
233
234         /* enqueue dma buffers */
235         dma_enqueue(substream);
236
237         return ret;
238 }
239
240 static int dma_trigger(struct snd_pcm_substream *substream, int cmd)
241 {
242         struct runtime_data *prtd = substream->runtime->private_data;
243         int ret = 0;
244
245         pr_debug("Entered %s\n", __func__);
246
247         spin_lock(&prtd->lock);
248
249         switch (cmd) {
250         case SNDRV_PCM_TRIGGER_START:
251         case SNDRV_PCM_TRIGGER_RESUME:
252         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
253                 prtd->state |= ST_RUNNING;
254                 prtd->params->ops->trigger(prtd->params->ch);
255                 break;
256
257         case SNDRV_PCM_TRIGGER_STOP:
258         case SNDRV_PCM_TRIGGER_SUSPEND:
259         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
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 u64 dma_mask = DMA_BIT_MASK(32);
404
405 static int dma_new(struct snd_soc_pcm_runtime *rtd)
406 {
407         struct snd_card *card = rtd->card->snd_card;
408         struct snd_pcm *pcm = rtd->pcm;
409         int ret = 0;
410
411         pr_debug("Entered %s\n", __func__);
412
413         if (!card->dev->dma_mask)
414                 card->dev->dma_mask = &dma_mask;
415         if (!card->dev->coherent_dma_mask)
416                 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
417
418         if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
419                 ret = preallocate_dma_buffer(pcm,
420                         SNDRV_PCM_STREAM_PLAYBACK);
421                 if (ret)
422                         goto out;
423         }
424
425         if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
426                 ret = preallocate_dma_buffer(pcm,
427                         SNDRV_PCM_STREAM_CAPTURE);
428                 if (ret)
429                         goto out;
430         }
431 out:
432         return ret;
433 }
434
435 static struct snd_soc_platform_driver samsung_asoc_platform = {
436         .ops            = &dma_ops,
437         .pcm_new        = dma_new,
438         .pcm_free       = dma_free_dma_buffers,
439 };
440
441 static int __devinit samsung_asoc_platform_probe(struct platform_device *pdev)
442 {
443         return snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform);
444 }
445
446 static int __devexit samsung_asoc_platform_remove(struct platform_device *pdev)
447 {
448         snd_soc_unregister_platform(&pdev->dev);
449         return 0;
450 }
451
452 static struct platform_driver asoc_dma_driver = {
453         .driver = {
454                 .name = "samsung-audio",
455                 .owner = THIS_MODULE,
456         },
457
458         .probe = samsung_asoc_platform_probe,
459         .remove = __devexit_p(samsung_asoc_platform_remove),
460 };
461
462 module_platform_driver(asoc_dma_driver);
463
464 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
465 MODULE_DESCRIPTION("Samsung ASoC DMA Driver");
466 MODULE_LICENSE("GPL");
467 MODULE_ALIAS("platform:samsung-audio");