📄 omap-audio-dma-intfc.c
字号:
/* * linux/sound/oss/omap-audio-dma-intfc.c * * Common audio DMA handling for the OMAP processors * * Copyright (C) 2004 Texas Instruments, Inc. * * Copyright (C) 2000, 2001 Nicolas Pitre <nico@cam.org> * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * History: * * 2004-06-07 Sriram Kannan - Created new file from omap_audio_dma_intfc.c. This file * will contain only the DMA interface and buffer handling of OMAP * audio driver. * * 2004-06-22 Sriram Kannan - removed legacy code (auto-init). Self-linking of DMA logical channel. * * 2004-08-12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 * and 2420 platforms. * * 2004-11-01 Nishanth Menon - 16xx,17xx platform code base modified to support multi channel chaining. */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/poll.h>#include <linux/pm.h>#include <linux/errno.h>#include <linux/sound.h>#include <linux/soundcard.h>#include <linux/sysrq.h>#include <linux/interrupt.h>#include <linux/dma-mapping.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/hardware.h>#include <asm/semaphore.h>#include <asm/arch/dma.h>#include "omap-audio-dma-intfc.h"#include <../drivers/ssi/omap-mcbsp.h>#include "omap-audio.h"#undef DEBUG//#define DEBUG#ifdef DEBUG#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS)#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__)#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n)#else#define DPRINTK( x... )#define FN_IN#define FN_OUT(x)#endif#define ERR(ARGS...) printk(KERN_ERR "{%s}-ERROR: ", __FUNCTION__);printk(ARGS);#define AUDIO_NAME "omap-audio"#define AUDIO_NBFRAGS_DEFAULT 8#define AUDIO_FRAGSIZE_DEFAULT 8192#define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref)#define SPIN_ADDR (dma_addr_t)0#define SPIN_SIZE 2048/* * IMPORTANT TODO: * This File has to be rearched once the dma interfaces have stabilized and integrated *//*********************** 1610,1710 ARCH SPECIFIC IMPLEMENTATION FOR THE TIME BEING **************/#if CONFIG_ARCH_OMAP16XX/* NOTE: This is to be improved to multichannel linking mode. */#define NUMBER_OF_CHANNELS_TO_LINK 2/* Channel Queue Handling macros * tail always points to the current free entry * Head always points to the current entry being used * end is either head or tail */#define AUDIO_QUEUE_INIT(s) s->dma_q_head = s->dma_q_tail = s->dma_q_count = 0;#define AUDIO_QUEUE_FULL(s) (NUMBER_OF_CHANNELS_TO_LINK == s->dma_q_count)#define AUDIO_QUEUE_EMPTY(s) (0 == s->dma_q_count)#define __AUDIO_INCREMENT_QUEUE(end) ((end)=((end)+1)%NUMBER_OF_CHANNELS_TO_LINK)#define AUDIO_INCREMENT_HEAD(s) __AUDIO_INCREMENT_QUEUE(s->dma_q_head); s->dma_q_count--;#define AUDIO_INCREMENT_TAIL(s) __AUDIO_INCREMENT_QUEUE(s->dma_q_tail); s->dma_q_count++;/* DMA buffer fragmentation sizes */#define MAX_DMA_SIZE 0x1000000#define CUT_DMA_SIZE 0x1000/* TODO: To be moved to more appropriate location */#define DCSR_ERROR 0x3#define DCSR_SYNC_SET (1 << 6)#define DCCR_FS (1 << 5)#define DCCR_PRIO (1 << 6)#define DCCR_EN (1 << 7)#define DCCR_AI (1 << 8)#define DCCR_REPEAT (1 << 9)/* if 0 the channel works in 3.1 compatible mode*/#define DCCR_N31COMP (1 << 10)#define DCCR_EP (1 << 11)#define DCCR_SRC_AMODE_BIT 12#define DCCR_SRC_AMODE_MASK (0x3<<12)#define DCCR_DST_AMODE_BIT 14#define DCCR_DST_AMODE_MASK (0x3<<14)#define AMODE_CONST 0x0#define AMODE_POST_INC 0x1#define AMODE_SINGLE_INDEX 0x2#define AMODE_DOUBLE_INDEX 0x3/**************************** DATA STRUCTURES *****************************************/static spinlock_t dma_list_lock = SPIN_LOCK_UNLOCKED;struct audio_isr_work_item { int current_lch; u16 ch_status; audio_stream_t *s;};static char work_item_running = 0;static struct audio_isr_work_item work1, work2;/* TODO: Remove the following function declarations as soon as they are declared *//* These should have been declared in dma.h */dma_addr_t omap_get_dma_pos(int lch);void omap_clear_dma(int lch);/*********************************** MODULE SPECIFIC FUNCTIONS PROTOTYPES *************/static void audio_dsr_handler(unsigned long);DECLARE_TASKLET(audio_isr_work1, audio_dsr_handler, (unsigned long)&work1);DECLARE_TASKLET(audio_isr_work2, audio_dsr_handler, (unsigned long)&work2);static void sound_dma_irq_handler(int lch, u16 ch_status, void *data);static void audio_dma_callback(int lch, u16 ch_status, void *data);static int omap_start_sound_dma(audio_stream_t * s, dma_addr_t dma_ptr, u_int size);static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr, u_int dma_size);static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr, u_int dma_size);static int audio_start_dma_chain(audio_stream_t * s);/*********************************** GLOBAL FUNCTIONS DEFINTIONS ***********************//*************************************************************************************** * * Buffer creation/destruction * **************************************************************************************/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, 0); 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; dmabuf += s->fragsize; dmaphys += s->fragsize; dmasize -= s->fragsize; } s->usr_head = s->dma_head = s->dma_tail = 0; AUDIO_QUEUE_INIT(s); s->started = 0; s->bytecount = 0; s->fragcount = 0; s->prevbuf = 0; sema_init(&s->sem, s->nbfrags); FN_OUT(0); return 0; err: 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);}/*************************************************************************************** * * DMA channel requests * **************************************************************************************/intomap_request_sound_dma(int device_id, const char *device_name, void *data, int **channels){ int i, err = 0; int *chan = NULL; FN_IN; if (unlikely((NULL == channels) || (NULL == device_name))) { BUG(); return -EPERM; } /* Try allocate memory for the num channels */ *channels = (int *)kmalloc(sizeof(int) * NUMBER_OF_CHANNELS_TO_LINK, GFP_KERNEL); chan = *channels; if (NULL == chan) { ERR("No Memory for channel allocs!\n"); FN_OUT(-ENOMEM); return -ENOMEM; } spin_lock(&dma_list_lock); for (i = 0; i < NUMBER_OF_CHANNELS_TO_LINK; i++) { err = omap_request_dma(device_id, device_name, sound_dma_irq_handler, data, &chan[i]); DPRINTK("%d- Channel got:%d\n", i, chan[i]); /* Handle Failure condition here */ if (err < 0) { int j; for (j = 0; j < i; j++) { omap_free_dma(chan[j]); } spin_unlock(&dma_list_lock); kfree(chan); *channels = NULL; ERR("Error in requesting channel %d=0x%x\n", i, err); FN_OUT(err); return err; } } /* Chain the channels together */ for (i = 0; i < NUMBER_OF_CHANNELS_TO_LINK; i++) { int cur_chan = chan[i]; int nex_chan = ((NUMBER_OF_CHANNELS_TO_LINK - 1 == i) ? chan[0] : chan[i + 1]); omap_dma_link_lch(cur_chan, nex_chan); } spin_unlock(&dma_list_lock); FN_OUT(0); return 0;}/*************************************************************************************** * * DMA channel requests Freeing * **************************************************************************************/int omap_free_sound_dma(int **channels){ int i; int *chan = NULL; FN_IN; if (unlikely(NULL == channels)) { BUG(); return -EPERM; } if (unlikely(NULL == *channels)) { BUG(); return -EPERM; } chan = (*channels); for (i = 0; i < NUMBER_OF_CHANNELS_TO_LINK; i++) { int cur_chan = chan[i]; int nex_chan = ((NUMBER_OF_CHANNELS_TO_LINK - 1 == i) ? chan[0] : chan[i + 1]); DPRINTK("Clean up %d\n", cur_chan); omap_stop_dma(cur_chan); omap_dma_unlink_lch(cur_chan, nex_chan); omap_free_dma(cur_chan); DPRINTK("%d- Channel freeing:%d %d\n", i, *((*channels) + i), chan[i]); } kfree(*channels); *channels = NULL; FN_OUT(0); return 0;}/*************************************************************************************** * * Process DMA requests - This will end up starting the transfer. Proper fragments of * Transfers will be initiated. * **************************************************************************************/int audio_process_dma(audio_stream_t * s){ int ret = 0; unsigned long flags; FN_IN; /* Dont let the ISR over ride touching the in_use flag */ local_irq_save(flags); if (1 == s->in_use) { local_irq_restore(flags); ERR("Called again while In Use\n"); return 0; } s->in_use = 1; local_irq_restore(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); if (s->stopped) goto spin; if (s->dma_spinref > 0 && s->pending_frags) { DPRINTK("CLEARING\n"); s->dma_spinref = 0; DMA_CLEAR(s); } while (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; ret = omap_start_sound_dma(s, b->dma_addr + b->offset, dma_size); if (ret) { goto process_out; } 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; } } spin: if (s->spin_idle) { int spincnt = 0; ERR("we are spinning\n"); while (omap_start_sound_dma(s, SPIN_ADDR, SPIN_SIZE) == 0) spincnt++; /* * Note: if there is still a data buffer being * processed then the ref count is negative. This * allows for the DMA termination to be accounted in * the proper order. Of course dma_spinref can't be * greater than 0 if dma_ref is not 0 since we kill * the spinning above as soon as there is real data to process. */ if (s->buffers && s->buffers[s->dma_tail].dma_ref) spincnt = -spincnt; s->dma_spinref += spincnt; } process_out: s->in_use = 0; FN_OUT(ret); return ret;}/*************************************************************************************** * * Prime Rx - Since the recieve buffer has no time limit as to when it would arrive, * we need to prime it * **************************************************************************************/void audio_prime_rx(audio_state_t * state){ audio_stream_t *is = state->input_stream; FN_IN; if (state->need_tx_for_rx) { /* * With some codecs like the Philips UDA1341 we must ensure * there is an output stream at any time while recording since * this is how the UDA1341 gets its clock from the SA1100. * So while there is no playback data to send, the output DMA * will spin with all zeroes. We use the cache flush special * area for that. */ state->output_stream->spin_idle = 1; audio_process_dma(state->output_stream); } is->pending_frags = is->nbfrags; sema_init(&is->sem, 0); is->active = 1; audio_process_dma(is); FN_OUT(0); return;}/*************************************************************************************** * * set the fragment size * **************************************************************************************/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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -