📄 omap-audio-dma-intfc.c
字号:
s->fragsize = 1 << val; if (s->nbfrags < 2) s->nbfrags = 2; if (s->nbfrags * s->fragsize > 128 * 1024) s->nbfrags = 128 * 1024 / s->fragsize; FN_OUT(0); if (audio_setup_buf(s)) return -ENOMEM; return val | (s->nbfrags << 16);}/*************************************************************************************** * * Sync up the buffers before we shutdown, else under-run errors will happen * **************************************************************************************/int audio_sync(struct file *file){ audio_state_t *state = file->private_data; audio_stream_t *s = state->output_stream; audio_buf_t *b; u_int shiftval = 0; unsigned long flags; DECLARE_WAITQUEUE(wait, current); FN_IN; if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped) { FN_OUT(1); return 0; } /* * Send current buffer if it contains data. Be sure to send * a full sample count. */ b = &s->buffers[s->usr_head]; if (b->offset &= ~3) { down(&s->sem); /* * HACK ALERT ! * To avoid increased complexity in the rest of the code * where full fragment sizes are assumed, we cheat a little * with the start pointer here and don't forget to restore * it later. */ shiftval = s->fragsize - b->offset; b->offset = shiftval; b->dma_addr -= shiftval; b->data -= shiftval; local_irq_save(flags); s->bytecount -= shiftval; if (++s->usr_head >= s->nbfrags) s->usr_head = 0; s->pending_frags++; audio_process_dma(s); local_irq_restore(flags); } /* Let's wait for all buffers to complete */ set_current_state(TASK_INTERRUPTIBLE); DPRINTK("---1"); add_wait_queue(&s->wq, &wait); DPRINTK("---2"); while ((s->pending_frags || (atomic_read(&s->sem.count) < s->nbfrags)) && !signal_pending(current)) { DPRINTK ("---3 - (s->pending_frags %d atomic_read(&s->sem.count) %d s->nbfrags %d !signal_pending(current)=%d\n", s->pending_frags, atomic_read(&s->sem.count), s->nbfrags, !signal_pending(current)); schedule(); set_current_state(TASK_INTERRUPTIBLE); } DPRINTK("---4"); set_current_state(TASK_RUNNING); remove_wait_queue(&s->wq, &wait); DPRINTK("---5"); /* undo the pointer hack above */ if (shiftval) { local_irq_save(flags); b->dma_addr += shiftval; b->data += shiftval; /* ensure sane DMA code behavior if not yet processed */ if (b->offset != 0) b->offset = s->fragsize; local_irq_restore(flags); } DPRINTK("---E\n"); FN_OUT(0); return 0;}/*************************************************************************************** * * Stop all the DMA channels of the stream * **************************************************************************************/void audio_stop_dma(audio_stream_t * s){ int *chan = s->lch; int i; FN_IN; if (unlikely(NULL == chan)) { BUG(); return; } for (i = 0; i < NUMBER_OF_CHANNELS_TO_LINK; i++) { int cur_chan = chan[i]; omap_stop_dma(cur_chan); DPRINTK("%d- Channel Stop:%d\n", i, chan[i]); } s->started = 0; FN_OUT(0); return;}/*************************************************************************************** * * Get the dma posn * **************************************************************************************/u_int audio_get_dma_pos(audio_stream_t * s){ audio_buf_t *b = &s->buffers[s->dma_tail]; u_int offset; FN_IN; if (b->dma_ref) { offset = omap_get_dma_pos(s->lch[s->dma_q_head]) - b->dma_addr; if (offset >= s->fragsize) offset = s->fragsize - 4; } else if (s->pending_frags) { offset = b->offset; } else { offset = 0; } FN_OUT(offset); return offset;}/*************************************************************************************** * * Reset the audio buffers * **************************************************************************************/void audio_reset(audio_stream_t * s){ FN_IN; if (s->buffers) { audio_stop_dma(s); s->buffers[s->dma_head].offset = 0; s->buffers[s->usr_head].offset = 0; s->usr_head = s->dma_head; s->pending_frags = s->prevbuf = 0; sema_init(&s->sem, s->nbfrags); } s->active = 0; s->stopped = 0; s->started = 0; FN_OUT(0); return;}/*************************************************************************************** * * Clear any pending transfers * **************************************************************************************/void omap_clear_sound_dma(audio_stream_t * s){ FN_IN; omap_clear_dma(s->lch[s->dma_q_head]); FN_OUT(0); return;}/*********************************** MODULE FUNCTIONS DEFINTIONS ***********************/#ifdef OMAP1610_MCBSP1_BASE#undef OMAP1610_MCBSP1_BASE#endif#define OMAP1610_MCBSP1_BASE 0xE1011000/*************************************************************************************** * * DMA related functions * **************************************************************************************/static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr, u_int dma_size){ int dt = 0x1; /* data type 16 */ int cen = 32; /* Stereo */ int cfn = dma_size / (2 * cen); FN_IN; DPRINTK(" Params are: channel=%d,dt=%d,cen=%d,cfn=%d,dma_size=%d\n", channel, dt, cen, cfn, dma_size); omap_set_dma_dest_params(channel, 0x05, 0x00, (OMAP1610_MCBSP1_BASE + 0x806)); omap_set_dma_src_params(channel, 0x00, 0x01, dma_ptr); omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00); FN_OUT(0); return 0;}static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr, u_int dma_size){ int dt = 0x1; /* data type 16 */ int cen = 16; /* mono */ int cfn = dma_size / (2 * cen); FN_IN; DPRINTK(" Params are: channel=%d,dt=%d,cen=%d,cfn=%d,dma_size=%d\n", channel, dt, cen, cfn, dma_size); omap_set_dma_src_params(channel, 0x05, 0x00, (OMAP1610_MCBSP1_BASE + 0x802)); omap_set_dma_dest_params(channel, 0x00, 0x01, dma_ptr); omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00); FN_OUT(0); return 0;}static int audio_start_dma_chain(audio_stream_t * s){ int channel = s->lch[s->dma_q_head]; FN_IN; if (!s->started) { DPRINTK("STARTDMA %d\n", channel); omap_start_dma(channel); s->started = 1; } /* else the dma itself will progress forward with out our help */ FN_OUT(0); return 0;}/* Start DMA - * Do the initial set of work to initialize all the channels as required. * We shall then initate a transfer */static int omap_start_sound_dma(audio_stream_t * s, dma_addr_t dma_ptr, u_int dma_size){ int ret = -EPERM; FN_IN; if (unlikely(dma_size > MAX_DMA_SIZE)) { ERR("DmaSoundDma: Start: overflowed %d-%d\n", dma_size, MAX_DMA_SIZE); return -EOVERFLOW; } DPRINTK("BEFORE CHECK head=%d, tail=%d\n", s->dma_q_head, s->dma_q_tail); if (AUDIO_QUEUE_FULL(s)) { DPRINTK("QUEUE FULL!\n"); ret = -2; goto sound_out; } if (s->input_or_output == FMODE_WRITE) /*playback */ { DPRINTK("PLAYBACK DMA : data ptr = %x , data size = %x\n", dma_ptr, dma_size); ret = audio_set_dma_params_play(s->lch[s->dma_q_tail], dma_ptr, dma_size); } else { DPRINTK ("CAPTURE DMA : data ptr = %x , data size = %x\n", dma_ptr, dma_size); ret = audio_set_dma_params_capture(s->lch[s->dma_q_tail], dma_ptr, dma_size); } if (ret != 0) { DPRINTK("set_params cannot be done, trying again ...\n"); ret = -2; /* indicate queue full */ goto sound_out; } AUDIO_INCREMENT_TAIL(s); DPRINTK("AFTER START head=%d, tail=%d [%d]\n", s->dma_q_head, s->dma_q_tail, s->lch[s->dma_q_head]); ret = audio_start_dma_chain(s); DPRINTK("\n OMAP_AUDIO : chain started"); if (ret) { ERR("dma start failed"); } sound_out: FN_OUT(ret); return ret;}/*************************************************************************************** * * ISR related functions * **************************************************************************************//* The work item handler */static void audio_dsr_handler(unsigned long inData){ void *data = (void *)inData; struct audio_isr_work_item *work = data; audio_stream_t *s = (work->s); int sound_curr_lch = work->current_lch; u16 ch_status = work->ch_status; FN_IN; DPRINTK("DS%d\n", sound_curr_lch); DPRINTK("lch=%d,status=0x%x, data=%p as=%p\n", sound_curr_lch, ch_status, data, s); if (AUDIO_QUEUE_EMPTY(s)) { ERR("Interrupt(%d) for empty queue(h=%d, T=%d)???\n", sound_curr_lch, s->dma_q_head, s->dma_q_tail); ERR("nbfrag=%d,pendfrags=%d,USR-H=%d, QH-%d QT-%d\n", s->nbfrags, s->pending_frags, s->usr_head, s->dma_head, s->dma_tail); FN_OUT(-1); return; } AUDIO_INCREMENT_HEAD(s); /* Empty the queue */ /* Try to fill again */ audio_dma_callback(sound_curr_lch, ch_status, s); FN_OUT(0);}/* Macro to trace the IRQ calls - checks for multi-channel irqs *///#define IRQ_TRACE#ifdef IRQ_TRACE#define MAX_UP 10static char xyz[MAX_UP] = { 0 };static int h = 0;#endif/* ISRs have to be short and smart.. So we transfer every heavy duty stuff to the * work item */static void sound_dma_irq_handler(int sound_curr_lch, u16 ch_status, void *data){ int dma_status = ch_status; audio_stream_t *s = (audio_stream_t *) data; FN_IN;#ifdef IRQ_TRACE xyz[h++] = '0' + sound_curr_lch; if (h == MAX_UP - 1) { printk("%s-", xyz); h = 0; }#endif DPRINTK("lch=%d,status=0x%x, dma_status=%d, data=%p\n", sound_curr_lch, ch_status, dma_status, data); if (dma_status & (DCSR_ERROR)) { omap_writew(omap_readw(OMAP_DMA_CCR(sound_curr_lch)) & ~DCCR_EN, OMAP_DMA_CCR(sound_curr_lch)); ERR("DCSR_ERROR!\n"); FN_OUT(-1); return; } /* Start the work item - we ping pong the work items */ if (!work_item_running) { work1.current_lch = sound_curr_lch; work1.ch_status = ch_status; work1.s = s; /* schedule tasklet 1 */ tasklet_schedule(&audio_isr_work1); work_item_running = 1; } else { work2.current_lch = sound_curr_lch; work2.ch_status = ch_status; work2.s = s; /* schedule tasklet 2 */ tasklet_schedule(&audio_isr_work2); work_item_running = 0; } FN_OUT(0); return;}/* The call back that handles buffer stuff */static void audio_dma_callback(int lch, u16 ch_status, void *data){ audio_stream_t *s = data; audio_buf_t *b = &s->buffers[s->dma_tail]; FN_IN; DPRINTK(": b->dma_ref = %d, b->offset = %d\n", b->dma_ref, b->offset); if (s->dma_spinref > 0) { s->dma_spinref--; } else if (!s->buffers) { printk(KERN_CRIT "omap_audio: received DMA IRQ for non existent buffers!\n"); return; } else if (b->dma_ref && --b->dma_ref == 0 && b->offset >= s->fragsize) { /* This fragment is done */ b->offset = 0; s->bytecount += s->fragsize; s->fragcount++; s->dma_spinref = -s->dma_spinref; if (++s->dma_tail >= s->nbfrags) s->dma_tail = 0; if (!s->mapped) up(&s->sem); else s->pending_frags++; wake_up(&s->wq); } /* Detect End of transfer and Clean up * Pend frags will be 1 . This means that there is one more chunk of data to * complete transmission. This implies if channel 1 and channel 2 have data, * the callback is for channel1 and channel 2 is currently being transmitted * by the DMA. If we wait for Channel 2's completion before stopping the DMA, * we will be late since the channels are chained, and the channel 1 would * have already started trasmitting (junk data), hence a spurious call back * would occur (for channel 1). Hence we forgo the last buffer transmission. */ if ((s->pending_frags < s->prevbuf) && (s->pending_frags <= (NUMBER_OF_CHANNELS_TO_LINK - 1))) { DPRINTK("%s Stop pend=%d prev=%d\n", __FUNCTION__, s->pending_frags, s->prevbuf); omap_stop_dma(s->lch[s->dma_q_head]); omap_stop_dma(s->lch[s->dma_q_tail]); //audio_queue_count =0; AUDIO_QUEUE_INIT(s); b->offset = 0; s->bytecount += s->fragsize; s->fragcount++; if (++s->dma_tail >= s->nbfrags) s->dma_tail = 0; if (!s->mapped) { while (s->nbfrags != atomic_read(&s->sem.count)) up(&s->sem); } s->prevbuf = s->pending_frags = 0; wake_up(&s->wq); s->started = 0; } else { s->prevbuf = s->pending_frags; audio_process_dma(s); } FN_OUT(0); return;}#endif /* End of #if CONFIG_ARCH_OMAP16XX *//******************* THE FOLLOWING IS FOR OMAP24xx **************************/#ifdef CONFIG_ARCH_OMAP24XX#define MAX_DMA_SIZE 0x2000#define CUT_DMA_SIZE 0x2000static void audio_dma_callback(int lch, u16 ch_status, void *data){ audio_stream_t *s = data; audio_buf_t *b = &s->buffers[s->dma_tail]; DPRINTK(": b->dma_ref = %d, b->offset = %d\n", b->dma_ref, b->offset); if (!s->buffers) { printk(KERN_CRIT "omap_audio: received DMA IRQ for non existent buffers!\n"); return; } else if (b->dma_ref && --b->dma_ref == 0 && b->offset >= s->fragsize) { /* This fragment is done */ b->offset = 0; s->bytecount += s->fragsize;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -