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