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