📄 omap-audio-dma-intfc.c
字号:
s->fragcount++; if (++s->dma_tail >= s->nbfrags) s->dma_tail = 0; if (!s->mapped) up(&s->sem); else s->pending_frags++; wake_up(&s->wq); } /* * Since multiple channels are working in a ping-pong fashion, * we have a scenario where we have filled all the buffers, yet we * expect two callbacks (1 for previous filled buffer and one for last * filled buffer). These will happen one after the next. * Disable the channels as soon as we detect that we are completing the * link */ if (1 >= s->pending_frags) { DPRINTK ("\n OMAP_AUDIO : callback we are stopping the chain for %d \n", lch); omap_stop_dma(lch); disable_chain(lch); } else { DPRINTK ("\n OMAP_AUDIO : audio_process_dma called from callback"); audio_process_dma(s); } FN_OUT(0);}static intaudio_set_dma_params_play(audio_stream_t * s, lch_chain * dma_chain, int pos, int size){ int ret; dma_channel_params params; params.data_type = 0x1; /* data type 16 */ params.elem_count = 32; params.frame_count = s->fragsize / (2 * params.elem_count); params.src_amode = 1; /* post increment */ params.src_start = pos; params.src_ei = 0; params.src_fi = 0; params.dst_amode = 0; /* constant */ /* mcbsp */ params.dst_start = AUDIO_MCBSP_DATAWRITE; params.trigger = AUDIO_DMA_TX; params.dst_ei = 0; params.dst_fi = 0; params.sync_mode = 0x0; params.src_or_dst_synch = 0; /* DEST synch */ ret = omap_set_dma_params_chain(dma_chain, params); return ret;}static intaudio_set_dma_params_capture(audio_stream_t * s, lch_chain * dma_chain, int pos, int size){ int ret; dma_channel_params params; params.data_type = 0x1; /* data type 16 */ params.elem_count = 32; params.frame_count = s->fragsize / (2 * params.elem_count); params.src_amode = 0; /* post increment */ /* mcbsp */ params.src_start = AUDIO_MCBSP_DATAREAD; params.trigger = AUDIO_DMA_RX; params.src_ei = 0; params.src_fi = 0; params.dst_amode = 1; params.dst_start = pos; params.dst_ei = 0; params.dst_fi = 0; params.sync_mode = 0x0; params.src_or_dst_synch = 1; /* SRC SYNCH */ ret = omap_set_dma_params_chain(dma_chain, params); return ret;}int audio_process_dma(audio_stream_t * s){ int ret; unsigned long flags; DPRINTK (": pending_frags = %d, usr_head = %d, dma_head = %d, dma_tail = %d, bytecount = %d, fragcount = %d\n", s->pending_frags, s->usr_head, s->dma_head, s->dma_tail, s->bytecount, s->fragcount); local_irq_save(flags); while ((!s->stopped) && (s->pending_frags)) { audio_buf_t *b = &s->buffers[s->dma_head]; u_int dma_size = s->fragsize - b->offset; if (dma_size > MAX_DMA_SIZE) dma_size = CUT_DMA_SIZE; if (s->input_or_output == FMODE_WRITE) /*playback */ { DPRINTK ("\n PLAYBACK DMA : data ptr = %x , data size = %x", b->dma_addr + b->offset, dma_size); ret = audio_set_dma_params_play(s, s->dma_chain, b->dma_addr + b->offset, dma_size); } else { DPRINTK ("\n CAPTURE DMA : data ptr = %x , data size = %x", b->dma_addr + b->offset, dma_size); ret = audio_set_dma_params_capture(s, s->dma_chain, b->dma_addr + b->offset, dma_size); } if (ret != 0) { DPRINTK ("\nERROR: omap_audio.c: set_params cannot be done, trying again ...\n"); local_irq_restore(flags); return -2; /* indicate queue full */ } ret = omap_start_dma_chain(s->dma_chain); DPRINTK("\n OMAP_AUDIO : chain started"); if (ret) { FN_OUT(1); printk("\n Error: OMAP_AUDIO : dma start failed"); local_irq_restore(flags); return -1; } b->dma_ref++; b->offset += dma_size; if (b->offset >= s->fragsize) { s->pending_frags--; if (++s->dma_head >= s->nbfrags) s->dma_head = 0; } } local_irq_restore(flags); FN_OUT(0); return 0;}int audio_setup_buf(audio_stream_t * s){ int frag; int dmasize = 0; char *dmabuf = NULL; dma_addr_t dmaphys = 0; FN_IN; if (s->buffers) { FN_OUT(1); return -EBUSY; } s->buffers = kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL); if (!s->buffers) goto err; memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags); for (frag = 0; frag < s->nbfrags; frag++) { audio_buf_t *b = &s->buffers[frag]; /* * Let's allocate non-cached memory for DMA buffers. * We try to allocate all memory at once. * If this fails (a common reason is memory fragmentation), * then we allocate more smaller buffers. */ if (!dmasize) { dmasize = (s->nbfrags - frag) * s->fragsize; do { dmabuf = dma_alloc_coherent(NULL, dmasize, &dmaphys, GFP_KERNEL | GFP_DMA); if (!dmabuf) dmasize -= s->fragsize; } while (!dmabuf && dmasize); if (!dmabuf) goto err; b->master = dmasize; memzero(dmabuf, dmasize); } b->data = dmabuf; b->dma_addr = dmaphys; DPRINTK("buf %d: start %p dma %#08x master %d fragsize %d\n", frag, b->data, b->dma_addr, b->master, s->fragsize); dmabuf += s->fragsize; dmaphys += s->fragsize; dmasize -= s->fragsize; } s->usr_head = s->dma_head = s->dma_tail = 0; s->bytecount = 0; s->fragcount = 0; sema_init(&s->sem, s->nbfrags); FN_OUT(0); return 0; err: printk(AUDIO_NAME ": unable to allocate audio memory\n "); audio_discard_buf(s); FN_OUT(1); return -ENOMEM;}void audio_discard_buf(audio_stream_t * s){ FN_IN; /* ensure DMA isn't using those buffers */ audio_reset(s); if (s->buffers) { int frag; for (frag = 0; frag < s->nbfrags; frag++) { if (!s->buffers[frag].master) continue; dma_free_coherent(NULL, s->buffers[frag].master, s->buffers[frag].data, s->buffers[frag].dma_addr); } kfree(s->buffers); s->buffers = NULL; } FN_OUT(0);}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) { if (s->input_or_output == FMODE_WRITE) offset = omap_get_dma_pos_src(s->dma_chain->queue_head) - b->dma_addr; else offset = omap_get_dma_pos_dst(s->dma_chain->queue_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;}void audio_prime_rx(audio_state_t * state){ audio_stream_t *is = state->input_stream; unsigned long flags; FN_IN; local_irq_save(flags); is->pending_frags = is->nbfrags; sema_init(&is->sem, 0); is->active = 1; audio_process_dma(is); local_irq_restore(flags); FN_OUT(0);}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 = 0; sema_init(&s->sem, s->nbfrags); } s->active = 0; s->stopped = 0; FN_OUT(0);}int audio_set_fragments(audio_stream_t * s, int val){ FN_IN;; if (s->active) return -EBUSY; if (s->buffers) audio_discard_buf(s); s->nbfrags = (val >> 16) & 0x7FFF; val &= 0xFFFF; if (val < 4) val = 4; if (val > 15) val = 15; 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);}void audio_stop_dma(audio_stream_t * s){ u_int pos; unsigned long flags; audio_buf_t *b; FN_IN; if (s->dma_spinref > 0 || !s->buffers) return; local_irq_save(flags); s->stopped = 1; omap_stop_dma_chain(s->dma_chain); pos = audio_get_dma_pos(s); omap_clear_dma_chain(s->dma_chain); local_irq_restore(flags); /* back up pointers to be ready to restart from the same spot */ while (s->dma_head != s->dma_tail) { b = &s->buffers[s->dma_head]; if (b->dma_ref) { b->dma_ref = 0; b->offset = 0; } s->pending_frags++; if (s->dma_head == 0) s->dma_head = s->nbfrags; s->dma_head--; } b = &s->buffers[s->dma_head]; if (b->dma_ref) { b->offset = pos; b->dma_ref = 0; } FN_OUT(0);}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; s->bytecount -= shiftval; if (++s->usr_head >= s->nbfrags) s->usr_head = 0; local_irq_save(flags); 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 pf=%d sc=%d nbfg=%d sp=%d ", s->pending_frags, atomic_read(&s->sem.count), s->nbfrags, signal_pending(current)); schedule(); DPRINTK("---4"); set_current_state(TASK_INTERRUPTIBLE); DPRINTK("---5"); } set_current_state(TASK_RUNNING); remove_wait_queue(&s->wq, &wait); /* 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); } FN_OUT(0); return 0;}#endif /* End of #ifdef CONFIG_ARCH_OMAP24XX *//********************************************************************************* * * audio_get_dma_callback(): return the dma interface call back function * *********************************************************************************/dma_callback_t audio_get_dma_callback(void){ FN_IN; FN_OUT(0); return audio_dma_callback;}MODULE_AUTHOR("Texas Instruments");MODULE_DESCRIPTION("Common DMA handling for Audio driver on OMAP processors");MODULE_LICENSE("GPL");#if CONFIG_ARCH_OMAP16XXEXPORT_SYMBOL(omap_clear_sound_dma);EXPORT_SYMBOL(omap_request_sound_dma);EXPORT_SYMBOL(omap_free_sound_dma);#endif /* End of #if CONFIG_ARCH_OMAP16XX */EXPORT_SYMBOL(audio_get_dma_callback);EXPORT_SYMBOL(audio_setup_buf);EXPORT_SYMBOL(audio_process_dma);EXPORT_SYMBOL(audio_prime_rx);EXPORT_SYMBOL(audio_set_fragments);EXPORT_SYMBOL(audio_sync);EXPORT_SYMBOL(audio_stop_dma);EXPORT_SYMBOL(audio_get_dma_pos);EXPORT_SYMBOL(audio_reset);EXPORT_SYMBOL(audio_discard_buf);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -