⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 omap-audio-dma-intfc.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * 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 + -