tree-wide: fix assorted typos all over the place
[linux-2.6.git] / sound / soc / s6000 / s6000-pcm.c
1 /*
2  * ALSA PCM interface for the Stetch s6000 family
3  *
4  * Author:      Daniel Gloeckner, <dg@emlix.com>
5  * Copyright:   (C) 2009 emlix GmbH <info@emlix.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/platform_device.h>
15 #include <linux/slab.h>
16 #include <linux/dma-mapping.h>
17 #include <linux/interrupt.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 <asm/dma.h>
25 #include <variant/dmac.h>
26
27 #include "s6000-pcm.h"
28
29 #define S6_PCM_PREALLOCATE_SIZE (96 * 1024)
30 #define S6_PCM_PREALLOCATE_MAX  (2048 * 1024)
31
32 static struct snd_pcm_hardware s6000_pcm_hardware = {
33         .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
34                  SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
35                  SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_JOINT_DUPLEX),
36         .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE),
37         .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \
38                   SNDRV_PCM_RATE_8000_192000),
39         .rate_min = 0,
40         .rate_max = 1562500,
41         .channels_min = 2,
42         .channels_max = 8,
43         .buffer_bytes_max = 0x7ffffff0,
44         .period_bytes_min = 16,
45         .period_bytes_max = 0xfffff0,
46         .periods_min = 2,
47         .periods_max = 1024, /* no limit */
48         .fifo_size = 0,
49 };
50
51 struct s6000_runtime_data {
52         spinlock_t lock;
53         int period;             /* current DMA period */
54 };
55
56 static void s6000_pcm_enqueue_dma(struct snd_pcm_substream *substream)
57 {
58         struct snd_pcm_runtime *runtime = substream->runtime;
59         struct s6000_runtime_data *prtd = runtime->private_data;
60         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
61         struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
62         int channel;
63         unsigned int period_size;
64         unsigned int dma_offset;
65         dma_addr_t dma_pos;
66         dma_addr_t src, dst;
67
68         period_size = snd_pcm_lib_period_bytes(substream);
69         dma_offset = prtd->period * period_size;
70         dma_pos = runtime->dma_addr + dma_offset;
71
72         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
73                 src = dma_pos;
74                 dst = par->sif_out;
75                 channel = par->dma_out;
76         } else {
77                 src = par->sif_in;
78                 dst = dma_pos;
79                 channel = par->dma_in;
80         }
81
82         if (!s6dmac_channel_enabled(DMA_MASK_DMAC(channel),
83                                     DMA_INDEX_CHNL(channel)))
84                 return;
85
86         if (s6dmac_fifo_full(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel))) {
87                 printk(KERN_ERR "s6000-pcm: fifo full\n");
88                 return;
89         }
90
91         BUG_ON(period_size & 15);
92         s6dmac_put_fifo(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel),
93                         src, dst, period_size);
94
95         prtd->period++;
96         if (unlikely(prtd->period >= runtime->periods))
97                 prtd->period = 0;
98 }
99
100 static irqreturn_t s6000_pcm_irq(int irq, void *data)
101 {
102         struct snd_pcm *pcm = data;
103         struct snd_soc_pcm_runtime *runtime = pcm->private_data;
104         struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
105         struct s6000_runtime_data *prtd;
106         unsigned int has_xrun;
107         int i, ret = IRQ_NONE;
108         u32 channel[2] = {
109                 [SNDRV_PCM_STREAM_PLAYBACK] = params->dma_out,
110                 [SNDRV_PCM_STREAM_CAPTURE] = params->dma_in
111         };
112
113         has_xrun = params->check_xrun(runtime->dai->cpu_dai);
114
115         for (i = 0; i < ARRAY_SIZE(channel); ++i) {
116                 struct snd_pcm_substream *substream = pcm->streams[i].substream;
117                 unsigned int pending;
118
119                 if (!channel[i])
120                         continue;
121
122                 if (unlikely(has_xrun & (1 << i)) &&
123                     substream->runtime &&
124                     snd_pcm_running(substream)) {
125                         dev_dbg(pcm->dev, "xrun\n");
126                         snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
127                         ret = IRQ_HANDLED;
128                 }
129
130                 pending = s6dmac_int_sources(DMA_MASK_DMAC(channel[i]),
131                                              DMA_INDEX_CHNL(channel[i]));
132
133                 if (pending & 1) {
134                         ret = IRQ_HANDLED;
135                         if (likely(substream->runtime &&
136                                    snd_pcm_running(substream))) {
137                                 snd_pcm_period_elapsed(substream);
138                                 dev_dbg(pcm->dev, "period elapsed %x %x\n",
139                                        s6dmac_cur_src(DMA_MASK_DMAC(channel[i]),
140                                                    DMA_INDEX_CHNL(channel[i])),
141                                        s6dmac_cur_dst(DMA_MASK_DMAC(channel[i]),
142                                                    DMA_INDEX_CHNL(channel[i])));
143                                 prtd = substream->runtime->private_data;
144                                 spin_lock(&prtd->lock);
145                                 s6000_pcm_enqueue_dma(substream);
146                                 spin_unlock(&prtd->lock);
147                         }
148                 }
149
150                 if (unlikely(pending & ~7)) {
151                         if (pending & (1 << 3))
152                                 printk(KERN_WARNING
153                                        "s6000-pcm: DMA %x Underflow\n",
154                                        channel[i]);
155                         if (pending & (1 << 4))
156                                 printk(KERN_WARNING
157                                        "s6000-pcm: DMA %x Overflow\n",
158                                        channel[i]);
159                         if (pending & 0x1e0)
160                                 printk(KERN_WARNING
161                                        "s6000-pcm: DMA %x Master Error "
162                                        "(mask %x)\n",
163                                        channel[i], pending >> 5);
164
165                 }
166         }
167
168         return ret;
169 }
170
171 static int s6000_pcm_start(struct snd_pcm_substream *substream)
172 {
173         struct s6000_runtime_data *prtd = substream->runtime->private_data;
174         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
175         struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
176         unsigned long flags;
177         int srcinc;
178         u32 dma;
179
180         spin_lock_irqsave(&prtd->lock, flags);
181
182         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
183                 srcinc = 1;
184                 dma = par->dma_out;
185         } else {
186                 srcinc = 0;
187                 dma = par->dma_in;
188         }
189         s6dmac_enable_chan(DMA_MASK_DMAC(dma), DMA_INDEX_CHNL(dma),
190                            1 /* priority 1 (0 is max) */,
191                            0 /* peripheral requests w/o xfer length mode */,
192                            srcinc /* source address increment */,
193                            srcinc^1 /* destination address increment */,
194                            0 /* chunksize 0 (skip impossible on this dma) */,
195                            0 /* source skip after chunk (impossible) */,
196                            0 /* destination skip after chunk (impossible) */,
197                            4 /* 16 byte burst size */,
198                            -1 /* don't conserve bandwidth */,
199                            0 /* low watermark irq descriptor threshold */,
200                            0 /* disable hardware timestamps */,
201                            1 /* enable channel */);
202
203         s6000_pcm_enqueue_dma(substream);
204         s6000_pcm_enqueue_dma(substream);
205
206         spin_unlock_irqrestore(&prtd->lock, flags);
207
208         return 0;
209 }
210
211 static int s6000_pcm_stop(struct snd_pcm_substream *substream)
212 {
213         struct s6000_runtime_data *prtd = substream->runtime->private_data;
214         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
215         struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
216         unsigned long flags;
217         u32 channel;
218
219         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
220                 channel = par->dma_out;
221         else
222                 channel = par->dma_in;
223
224         s6dmac_set_terminal_count(DMA_MASK_DMAC(channel),
225                                   DMA_INDEX_CHNL(channel), 0);
226
227         spin_lock_irqsave(&prtd->lock, flags);
228
229         s6dmac_disable_chan(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel));
230
231         spin_unlock_irqrestore(&prtd->lock, flags);
232
233         return 0;
234 }
235
236 static int s6000_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
237 {
238         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
239         struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
240         int ret;
241
242         ret = par->trigger(substream, cmd, 0);
243         if (ret < 0)
244                 return ret;
245
246         switch (cmd) {
247         case SNDRV_PCM_TRIGGER_START:
248         case SNDRV_PCM_TRIGGER_RESUME:
249         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
250                 ret = s6000_pcm_start(substream);
251                 break;
252         case SNDRV_PCM_TRIGGER_STOP:
253         case SNDRV_PCM_TRIGGER_SUSPEND:
254         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
255                 ret = s6000_pcm_stop(substream);
256                 break;
257         default:
258                 ret = -EINVAL;
259         }
260         if (ret < 0)
261                 return ret;
262
263         return par->trigger(substream, cmd, 1);
264 }
265
266 static int s6000_pcm_prepare(struct snd_pcm_substream *substream)
267 {
268         struct s6000_runtime_data *prtd = substream->runtime->private_data;
269
270         prtd->period = 0;
271
272         return 0;
273 }
274
275 static snd_pcm_uframes_t s6000_pcm_pointer(struct snd_pcm_substream *substream)
276 {
277         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
278         struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
279         struct snd_pcm_runtime *runtime = substream->runtime;
280         struct s6000_runtime_data *prtd = runtime->private_data;
281         unsigned long flags;
282         unsigned int offset;
283         dma_addr_t count;
284
285         spin_lock_irqsave(&prtd->lock, flags);
286
287         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
288                 count = s6dmac_cur_src(DMA_MASK_DMAC(par->dma_out),
289                                        DMA_INDEX_CHNL(par->dma_out));
290         else
291                 count = s6dmac_cur_dst(DMA_MASK_DMAC(par->dma_in),
292                                        DMA_INDEX_CHNL(par->dma_in));
293
294         count -= runtime->dma_addr;
295
296         spin_unlock_irqrestore(&prtd->lock, flags);
297
298         offset = bytes_to_frames(runtime, count);
299         if (unlikely(offset >= runtime->buffer_size))
300                 offset = 0;
301
302         return offset;
303 }
304
305 static int s6000_pcm_open(struct snd_pcm_substream *substream)
306 {
307         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
308         struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
309         struct snd_pcm_runtime *runtime = substream->runtime;
310         struct s6000_runtime_data *prtd;
311         int ret;
312
313         snd_soc_set_runtime_hwparams(substream, &s6000_pcm_hardware);
314
315         ret = snd_pcm_hw_constraint_step(runtime, 0,
316                                          SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 16);
317         if (ret < 0)
318                 return ret;
319         ret = snd_pcm_hw_constraint_step(runtime, 0,
320                                          SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
321         if (ret < 0)
322                 return ret;
323         ret = snd_pcm_hw_constraint_integer(runtime,
324                                             SNDRV_PCM_HW_PARAM_PERIODS);
325         if (ret < 0)
326                 return ret;
327
328         if (par->same_rate) {
329                 int rate;
330                 spin_lock(&par->lock); /* needed? */
331                 rate = par->rate;
332                 spin_unlock(&par->lock);
333                 if (rate != -1) {
334                         ret = snd_pcm_hw_constraint_minmax(runtime,
335                                                         SNDRV_PCM_HW_PARAM_RATE,
336                                                         rate, rate);
337                         if (ret < 0)
338                                 return ret;
339                 }
340         }
341
342         prtd = kzalloc(sizeof(struct s6000_runtime_data), GFP_KERNEL);
343         if (prtd == NULL)
344                 return -ENOMEM;
345
346         spin_lock_init(&prtd->lock);
347
348         runtime->private_data = prtd;
349
350         return 0;
351 }
352
353 static int s6000_pcm_close(struct snd_pcm_substream *substream)
354 {
355         struct snd_pcm_runtime *runtime = substream->runtime;
356         struct s6000_runtime_data *prtd = runtime->private_data;
357
358         kfree(prtd);
359
360         return 0;
361 }
362
363 static int s6000_pcm_hw_params(struct snd_pcm_substream *substream,
364                                  struct snd_pcm_hw_params *hw_params)
365 {
366         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
367         struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
368         int ret;
369         ret = snd_pcm_lib_malloc_pages(substream,
370                                        params_buffer_bytes(hw_params));
371         if (ret < 0) {
372                 printk(KERN_WARNING "s6000-pcm: allocation of memory failed\n");
373                 return ret;
374         }
375
376         if (par->same_rate) {
377                 spin_lock(&par->lock);
378                 if (par->rate == -1 ||
379                     !(par->in_use & ~(1 << substream->stream))) {
380                         par->rate = params_rate(hw_params);
381                         par->in_use |= 1 << substream->stream;
382                 } else if (params_rate(hw_params) != par->rate) {
383                         snd_pcm_lib_free_pages(substream);
384                         par->in_use &= ~(1 << substream->stream);
385                         ret = -EBUSY;
386                 }
387                 spin_unlock(&par->lock);
388         }
389         return ret;
390 }
391
392 static int s6000_pcm_hw_free(struct snd_pcm_substream *substream)
393 {
394         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
395         struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
396
397         spin_lock(&par->lock);
398         par->in_use &= ~(1 << substream->stream);
399         if (!par->in_use)
400                 par->rate = -1;
401         spin_unlock(&par->lock);
402
403         return snd_pcm_lib_free_pages(substream);
404 }
405
406 static struct snd_pcm_ops s6000_pcm_ops = {
407         .open =         s6000_pcm_open,
408         .close =        s6000_pcm_close,
409         .ioctl =        snd_pcm_lib_ioctl,
410         .hw_params =    s6000_pcm_hw_params,
411         .hw_free =      s6000_pcm_hw_free,
412         .trigger =      s6000_pcm_trigger,
413         .prepare =      s6000_pcm_prepare,
414         .pointer =      s6000_pcm_pointer,
415 };
416
417 static void s6000_pcm_free(struct snd_pcm *pcm)
418 {
419         struct snd_soc_pcm_runtime *runtime = pcm->private_data;
420         struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
421
422         free_irq(params->irq, pcm);
423         snd_pcm_lib_preallocate_free_for_all(pcm);
424 }
425
426 static u64 s6000_pcm_dmamask = DMA_32BIT_MASK;
427
428 static int s6000_pcm_new(struct snd_card *card,
429                          struct snd_soc_dai *dai, struct snd_pcm *pcm)
430 {
431         struct snd_soc_pcm_runtime *runtime = pcm->private_data;
432         struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
433         int res;
434
435         if (!card->dev->dma_mask)
436                 card->dev->dma_mask = &s6000_pcm_dmamask;
437         if (!card->dev->coherent_dma_mask)
438                 card->dev->coherent_dma_mask = DMA_32BIT_MASK;
439
440         if (params->dma_in) {
441                 s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_in),
442                                     DMA_INDEX_CHNL(params->dma_in));
443                 s6dmac_int_sources(DMA_MASK_DMAC(params->dma_in),
444                                    DMA_INDEX_CHNL(params->dma_in));
445         }
446
447         if (params->dma_out) {
448                 s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_out),
449                                     DMA_INDEX_CHNL(params->dma_out));
450                 s6dmac_int_sources(DMA_MASK_DMAC(params->dma_out),
451                                    DMA_INDEX_CHNL(params->dma_out));
452         }
453
454         res = request_irq(params->irq, s6000_pcm_irq, IRQF_SHARED,
455                           s6000_soc_platform.name, pcm);
456         if (res) {
457                 printk(KERN_ERR "s6000-pcm couldn't get IRQ\n");
458                 return res;
459         }
460
461         res = snd_pcm_lib_preallocate_pages_for_all(pcm,
462                                                     SNDRV_DMA_TYPE_DEV,
463                                                     card->dev,
464                                                     S6_PCM_PREALLOCATE_SIZE,
465                                                     S6_PCM_PREALLOCATE_MAX);
466         if (res)
467                 printk(KERN_WARNING "s6000-pcm: preallocation failed\n");
468
469         spin_lock_init(&params->lock);
470         params->in_use = 0;
471         params->rate = -1;
472         return 0;
473 }
474
475 struct snd_soc_platform s6000_soc_platform = {
476         .name =         "s6000-audio",
477         .pcm_ops =      &s6000_pcm_ops,
478         .pcm_new =      s6000_pcm_new,
479         .pcm_free =     s6000_pcm_free,
480 };
481 EXPORT_SYMBOL_GPL(s6000_soc_platform);
482
483 static int __init s6000_pcm_init(void)
484 {
485         return snd_soc_register_platform(&s6000_soc_platform);
486 }
487 module_init(s6000_pcm_init);
488
489 static void __exit s6000_pcm_exit(void)
490 {
491         snd_soc_unregister_platform(&s6000_soc_platform);
492 }
493 module_exit(s6000_pcm_exit);
494
495 MODULE_AUTHOR("Daniel Gloeckner");
496 MODULE_DESCRIPTION("Stretch s6000 family PCM DMA module");
497 MODULE_LICENSE("GPL");