]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - sound/arm/omap/omap-alsa-dma.c
Merge current mainline tree into linux-omap tree
[linux-2.6-omap-h63xx.git] / sound / arm / omap / omap-alsa-dma.c
1 /*
2  * sound/arm/omap/omap-alsa-dma.c
3  *
4  * Common audio DMA handling for the OMAP processors
5  *
6  * Copyright (C) 2006 Mika Laitio <lamikr@cc.jyu.fi>
7  *
8  * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil
9  *
10  * Copyright (C) 2004 Texas Instruments, Inc.
11  *
12  * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org>
13  *
14  * This package is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License version 2 as
16  * published by the Free Software Foundation.
17  *
18  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
20  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21  *
22  * History:
23  *
24  * 2004-06-07   Sriram Kannan   - Created new file from omap_audio_dma_intfc.c.
25  *                              This file will contain only the DMA interface
26  *                              and buffer handling of OMAP audio driver.
27  *
28  * 2004-06-22   Sriram Kannan   - removed legacy code (auto-init). Self-linking
29  *                              of DMA logical channel.
30  *
31  * 2004-08-12   Nishanth Menon  - Modified to integrate Audio requirements on
32  *                              1610, 1710 platforms
33  *
34  * 2004-11-01   Nishanth Menon  - 16xx platform code base modified to support
35  *                              multi channel chaining.
36  *
37  * 2004-12-15   Nishanth Menon  - Improved 16xx platform channel logic
38  *                              introduced - tasklets, queue handling updated
39  *
40  * 2005-07-19   INdT Kernel Team - Alsa port. Creation of new file
41  *                              omap-alsa-dma.c based in omap-audio-dma-intfc.c
42  *                              oss file. Support for aic23 codec. Removal of
43  *                              buffer handling (Alsa does that), modifications
44  *                              in dma handling and port to alsa structures.
45  *
46  * 2005-12-18   Dirk Behme      - Added L/R Channel Interchange fix as proposed
47  *                              by Ajaya Babu
48  */
49
50 #include <linux/module.h>
51 #include <linux/init.h>
52 #include <linux/types.h>
53 #include <linux/fs.h>
54 #include <linux/mm.h>
55 #include <linux/slab.h>
56 #include <linux/sched.h>
57 #include <linux/poll.h>
58 #include <linux/pm.h>
59 #include <linux/errno.h>
60 #include <linux/sound.h>
61 #include <linux/soundcard.h>
62 #include <linux/sysrq.h>
63 #include <linux/interrupt.h>
64 #include <linux/dma-mapping.h>
65 #include <linux/io.h>
66 #include <linux/uaccess.h>
67
68 #include <asm/hardware.h>
69 #include <asm/semaphore.h>
70 #include <asm/arch/dma.h>
71 #include <asm/arch/mcbsp.h>
72 #include <asm/arch/omap-alsa.h>
73
74 #include "omap-alsa-dma.h"
75
76 #undef DEBUG
77
78 /*
79  * Channel Queue Handling macros
80  * tail always points to the current free entry
81  * Head always points to the current entry being used
82  * end is either head or tail
83  */
84
85 #define AUDIO_QUEUE_INIT(s) s->dma_q_head = s->dma_q_tail = s->dma_q_count = 0;
86 #define AUDIO_QUEUE_FULL(s) (nr_linked_channels == s->dma_q_count)
87 #define AUDIO_QUEUE_LAST(s) (1 == s->dma_q_count)
88 #define AUDIO_QUEUE_EMPTY(s) (0 == s->dma_q_count)
89 #define __AUDIO_INCREMENT_QUEUE(end) ((end) = ((end)+1) % nr_linked_channels)
90 #define AUDIO_INCREMENT_HEAD(s)                         \
91         do {                                            \
92                 __AUDIO_INCREMENT_QUEUE(s->dma_q_head); \
93                 s->dma_q_count--;                       \
94         } while (0)
95 #define AUDIO_INCREMENT_TAIL(s)                         \
96         do {                                            \
97                 __AUDIO_INCREMENT_QUEUE(s->dma_q_tail); \
98                 s->dma_q_count++;                       \
99         } while (0)
100
101 /* DMA buffer fragmentation sizes */
102 #define MAX_DMA_SIZE             0x1000000 /* todo: sync with alsa */
103 /* #define CUT_DMA_SIZE          0x1000 */
104 /* TODO: To be moved to more appropriate location */
105 #define DCSR_ERROR           0x3
106 #define DCSR_END_BLOCK       (1 << 5)
107 #define DCSR_SYNC_SET        (1 << 6)
108
109 #define DCCR_FS              (1 << 5)
110 #define DCCR_PRIO            (1 << 6)
111 #define DCCR_EN              (1 << 7)
112 #define DCCR_AI              (1 << 8)
113 #define DCCR_REPEAT          (1 << 9)
114 /* if 0 the channel works in 3.1 compatible mode */
115 #define DCCR_N31COMP         (1 << 10)
116 #define DCCR_EP              (1 << 11)
117 #define DCCR_SRC_AMODE_BIT   12
118 #define DCCR_SRC_AMODE_MASK  (0x3<<12)
119 #define DCCR_DST_AMODE_BIT   14
120 #define DCCR_DST_AMODE_MASK  (0x3<<14)
121 #define AMODE_CONST          0x0
122 #define AMODE_POST_INC       0x1
123 #define AMODE_SINGLE_INDEX   0x2
124 #define AMODE_DOUBLE_INDEX   0x3
125
126 /* Data structures */
127 DEFINE_SPINLOCK(dma_list_lock);
128 static char nr_linked_channels = 1;
129
130 /* Module specific functions */
131
132 static void sound_dma_irq_handler(int lch, u16 ch_status, void *data);
133 static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr,
134                                      u_int dma_size);
135 static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr,
136                                         u_int dma_size);
137 static int audio_start_dma_chain(struct audio_stream *s);
138
139 /*
140  * DMA channel requests
141  */
142 static void omap_sound_dma_link_lch(void *data)
143 {
144
145         struct audio_stream *s = (struct audio_stream *) data;
146         int *chan = s->lch;
147         int i;
148
149         FN_IN;
150         if (s->linked) {
151                 FN_OUT(1);
152                 return;
153         }
154         for (i = 0; i < nr_linked_channels; i++) {
155                 int cur_chan = chan[i];
156                 int nex_chan =
157                     ((nr_linked_channels - 1 ==
158                       i) ? chan[0] : chan[i + 1]);
159                 omap_dma_link_lch(cur_chan, nex_chan);
160         }
161         s->linked = 1;
162         FN_OUT(0);
163 }
164
165 int omap_request_alsa_sound_dma(int device_id, const char *device_name,
166                            void *data, int **channels)
167 {
168         int i, err = 0;
169         int *chan = NULL;
170         FN_IN;
171         if (unlikely((NULL == channels) || (NULL == device_name))) {
172                 BUG();
173                 return -EPERM;
174         }
175         /* Try allocate memory for the num channels */
176         *channels = kmalloc(sizeof(int) * nr_linked_channels, GFP_KERNEL);
177         chan = *channels;
178         if (NULL == chan) {
179                 ERR("No Memory for channel allocs!\n");
180                 FN_OUT(-ENOMEM);
181                 return -ENOMEM;
182         }
183         spin_lock(&dma_list_lock);
184         for (i = 0; i < nr_linked_channels; i++) {
185                 err = omap_request_dma(device_id,
186                                 device_name,
187                                 sound_dma_irq_handler,
188                                 data,
189                                 &chan[i]);
190
191                 /* Handle Failure condition here */
192                 if (err < 0) {
193                         int j;
194
195                         for (j = 0; j < i; j++)
196                                 omap_free_dma(chan[j]);
197
198                         spin_unlock(&dma_list_lock);
199                         kfree(chan);
200                         *channels = NULL;
201                         ERR("Error in requesting channel %d=0x%x\n", i,
202                             err);
203                         FN_OUT(err);
204                         return err;
205                 }
206         }
207
208         /* Chain the channels together */
209         if (!cpu_is_omap15xx())
210                 omap_sound_dma_link_lch(data);
211
212         spin_unlock(&dma_list_lock);
213         FN_OUT(0);
214         return 0;
215 }
216 EXPORT_SYMBOL(omap_request_alsa_sound_dma);
217
218 /*
219  * DMA channel requests Freeing
220  */
221 static void omap_sound_dma_unlink_lch(void *data)
222 {
223         struct audio_stream *s = (struct audio_stream *)data;
224         int *chan = s->lch;
225         int i;
226
227         FN_IN;
228         if (!s->linked) {
229                 FN_OUT(1);
230                 return;
231         }
232         for (i = 0; i < nr_linked_channels; i++) {
233                 int cur_chan = chan[i];
234                 int nex_chan =
235                     ((nr_linked_channels - 1 ==
236                       i) ? chan[0] : chan[i + 1]);
237                 omap_dma_unlink_lch(cur_chan, nex_chan);
238         }
239         s->linked = 0;
240         FN_OUT(0);
241 }
242
243 int omap_free_alsa_sound_dma(void *data, int **channels)
244 {
245         int i;
246         int *chan = NULL;
247
248         FN_IN;
249         if (unlikely(NULL == channels)) {
250                 BUG();
251                 return -EPERM;
252         }
253         if (unlikely(NULL == *channels)) {
254                 BUG();
255                 return -EPERM;
256         }
257         chan = (*channels);
258
259         if (!cpu_is_omap15xx())
260                 omap_sound_dma_unlink_lch(data);
261         for (i = 0; i < nr_linked_channels; i++) {
262                 int cur_chan = chan[i];
263                 omap_stop_dma(cur_chan);
264                 omap_free_dma(cur_chan);
265         }
266         kfree(*channels);
267         *channels = NULL;
268         FN_OUT(0);
269         return 0;
270 }
271 EXPORT_SYMBOL(omap_free_alsa_sound_dma);
272
273 /*
274  * Stop all the DMA channels of the stream
275  */
276 void omap_stop_alsa_sound_dma(struct audio_stream *s)
277 {
278         int *chan = s->lch;
279         int i;
280
281         FN_IN;
282         if (unlikely(NULL == chan)) {
283                 BUG();
284                 return;
285         }
286         for (i = 0; i < nr_linked_channels; i++) {
287                 int cur_chan = chan[i];
288                 omap_stop_dma(cur_chan);
289         }
290         s->started = 0;
291         FN_OUT(0);
292         return;
293 }
294 EXPORT_SYMBOL(omap_stop_alsa_sound_dma);
295
296 /*
297  * Clear any pending transfers
298  */
299 void omap_clear_alsa_sound_dma(struct audio_stream *s)
300 {
301         FN_IN;
302         omap_clear_dma(s->lch[s->dma_q_head]);
303         FN_OUT(0);
304         return;
305 }
306 EXPORT_SYMBOL(omap_clear_alsa_sound_dma);
307
308 /*
309  * DMA related functions
310  */
311 static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr,
312                                      u_int dma_size)
313 {
314         int dt = 0x1;           /* data type 16 */
315         int cen = 32;           /* Stereo */
316         int cfn = dma_size / (2 * cen);
317
318         FN_IN;
319         omap_set_dma_dest_params(channel, 0x05, 0x00,
320                                  (OMAP1510_MCBSP1_BASE + 0x06),
321                                  0, 0);
322         omap_set_dma_src_params(channel, 0x00, 0x01, dma_ptr,
323                                 0, 0);
324         omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00, 0, 0);
325         FN_OUT(0);
326         return 0;
327 }
328
329 static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr,
330                                         u_int dma_size)
331 {
332         int dt = 0x1;           /* data type 16 */
333         int cen = 32;           /* stereo */
334         int cfn = dma_size / (2 * cen);
335
336         FN_IN;
337         omap_set_dma_src_params(channel, 0x05, 0x00,
338                                 (OMAP1510_MCBSP1_BASE + 0x02),
339                                 0, 0);
340         omap_set_dma_dest_params(channel, 0x00, 0x01, dma_ptr, 0, 0);
341         omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00, 0, 0);
342         FN_OUT(0);
343         return 0;
344 }
345
346 static int audio_start_dma_chain(struct audio_stream *s)
347 {
348         int channel = s->lch[s->dma_q_head];
349         FN_IN;
350         if (!s->started) {
351                 s->hw_stop();      /* stops McBSP Interface */
352                 omap_start_dma(channel);
353                 s->started = 1;
354                 s->hw_start();     /* start McBSP interface */
355         } else if (cpu_is_omap310())
356                 omap_start_dma(channel);
357         /* else the dma itself will progress forward with out our help */
358         FN_OUT(0);
359         return 0;
360 }
361
362 /*
363  * Start DMA -
364  * Do the initial set of work to initialize all the channels as required.
365  * We shall then initate a transfer
366  */
367 int omap_start_alsa_sound_dma(struct audio_stream *s,
368                         dma_addr_t dma_ptr,
369                         u_int dma_size)
370 {
371         int ret = -EPERM;
372
373         FN_IN;
374
375         if (unlikely(dma_size > MAX_DMA_SIZE)) {
376                 ERR("DmaSoundDma: Start: overflowed %d-%d\n", dma_size,
377                     MAX_DMA_SIZE);
378                 return -EOVERFLOW;
379         }
380
381         if (s->stream_id == SNDRV_PCM_STREAM_PLAYBACK) {
382                 /* playback */
383                 ret =
384                     audio_set_dma_params_play(s->lch[s->dma_q_tail],
385                                               dma_ptr, dma_size);
386         } else {
387                 ret =
388                     audio_set_dma_params_capture(s->lch[s->dma_q_tail],
389                                                  dma_ptr, dma_size);
390         }
391         if (ret != 0) {
392                 ret = -3;       /* indicate queue full */
393                 goto sound_out;
394         }
395         AUDIO_INCREMENT_TAIL(s);
396         ret = audio_start_dma_chain(s);
397         if (ret)
398                 ERR("dma start failed");
399
400 sound_out:
401         FN_OUT(ret);
402         return ret;
403
404 }
405 EXPORT_SYMBOL(omap_start_alsa_sound_dma);
406
407 /*
408  * ISRs have to be short and smart..
409  * Here we call alsa handling, after some error checking
410  */
411 static void sound_dma_irq_handler(int sound_curr_lch, u16 ch_status,
412                                   void *data)
413 {
414         int dma_status = ch_status;
415         struct audio_stream *s = (struct audio_stream *) data;
416         FN_IN;
417
418         /* some register checking */
419         DPRINTK("lch=%d,status=0x%x, dma_status=%d, data=%p\n",
420                 sound_curr_lch, ch_status, dma_status, data);
421
422         if (dma_status & (DCSR_ERROR)) {
423                 OMAP_DMA_CCR_REG(sound_curr_lch) &= ~DCCR_EN;
424                 ERR("DCSR_ERROR!\n");
425                 FN_OUT(-1);
426                 return;
427         }
428
429         if (ch_status & DCSR_END_BLOCK)
430                 callback_omap_alsa_sound_dma(s);
431         FN_OUT(0);
432         return;
433 }
434
435 MODULE_AUTHOR("Texas Instruments");
436 MODULE_DESCRIPTION("Common DMA handling for Audio driver on OMAP processors");
437 MODULE_LICENSE("GPL");
438