swarm_cs4297a.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,962 行 · 第 1/5 页

C
1,962
字号
/*********************************************************************************      "swarm_cs4297a.c" --  Cirrus Logic-Crystal CS4297a linux audio driver.**      Copyright (C) 2001  Broadcom Corporation.*      Copyright (C) 2000,2001  Cirrus Logic Corp.  *            -- adapted from drivers by Thomas Sailer, *            -- but don't bug him; Problems should go to:*            -- tom woller (twoller@crystal.cirrus.com) or*               (audio@crystal.cirrus.com).*            -- adapted from cs4281 PCI driver for cs4297a on*               BCM1250 Synchronous Serial interface*               (kwalker@broadcom.com)**      This program is free software; you can redistribute it and/or modify*      it under the terms of the GNU General Public License as published by*      the Free Software Foundation; either version 2 of the License, or*      (at your option) any later version.**      This program is distributed in the hope that it will be useful,*      but WITHOUT ANY WARRANTY; without even the implied warranty of*      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the*      GNU General Public License for more details.**      You should have received a copy of the GNU General Public License*      along with this program; if not, write to the Free Software*      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.** Module command line parameters:*   none**  Supported devices:*  /dev/dsp    standard /dev/dsp device, (mostly) OSS compatible*  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible*  /dev/midi   simple MIDI UART interface, no ioctl** Modification History* 08/20/00 trw - silence and no stopping DAC until release* 08/23/00 trw - added CS_DBG statements, fix interrupt hang issue on DAC stop.* 09/18/00 trw - added 16bit only record with conversion * 09/24/00 trw - added Enhanced Full duplex (separate simultaneous *                capture/playback rates)* 10/03/00 trw - fixed mmap (fixed GRECORD and the XMMS mmap test plugin  *                libOSSm.so)* 10/11/00 trw - modified for 2.4.0-test9 kernel enhancements (NR_MAP removal)* 11/03/00 trw - fixed interrupt loss/stutter, added debug.* 11/10/00 bkz - added __devinit to cs4297a_hw_init()* 11/10/00 trw - fixed SMP and capture spinlock hang.* 12/04/00 trw - cleaned up CSDEBUG flags and added "defaultorder" moduleparm.* 12/05/00 trw - fixed polling (myth2), and added underrun swptr fix.* 12/08/00 trw - added PM support. * 12/14/00 trw - added wrapper code, builds under 2.4.0, 2.2.17-20, 2.2.17-8 *		 (RH/Dell base), 2.2.18, 2.2.12.  cleaned up code mods by ident.* 12/19/00 trw - added PM support for 2.2 base (apm_callback). other PM cleanup.* 12/21/00 trw - added fractional "defaultorder" inputs. if >100 then use *		 defaultorder-100 as power of 2 for the buffer size. example:*		 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size.********************************************************************************/#include <linux/list.h>#include <linux/module.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/sound.h>#include <linux/slab.h>#include <linux/soundcard.h>#include <linux/ac97_codec.h>#include <linux/pci.h>#include <linux/bitops.h>#include <linux/interrupt.h>#include <asm/io.h>#include <asm/dma.h>#include <linux/init.h>#include <linux/poll.h>#include <linux/smp_lock.h>#include <linux/wrapper.h>#include <asm/uaccess.h>#include <asm/sibyte/sb1250_regs.h>#include <asm/sibyte/sb1250_int.h>#include <asm/sibyte/sb1250_dma.h>#include <asm/sibyte/sb1250_scd.h>#include <asm/sibyte/sb1250_syncser.h>#include <asm/sibyte/sb1250_mac.h>#include <asm/sibyte/sb1250.h>#include <asm/sibyte/64bit.h>struct cs4297a_state;static void stop_dac(struct cs4297a_state *s);static void stop_adc(struct cs4297a_state *s);static void start_dac(struct cs4297a_state *s);static void start_adc(struct cs4297a_state *s);#undef OSS_DOCUMENTED_MIXER_SEMANTICS// --------------------------------------------------------------------- #define CS4297a_MAGIC           0xf00beef1// buffer order determines the size of the dma buffer for the driver.// under Linux, a smaller buffer allows more responsiveness from many of the // applications (e.g. games).  A larger buffer allows some of the apps (esound) // to not underrun the dma buffer as easily.  As default, use 32k (order=3)// rather than 64k as some of the games work more responsively.// log base 2( buff sz = 32k).//static unsigned long defaultorder = 3;//MODULE_PARM(defaultorder, "i");//// Turn on/off debugging compilation by commenting out "#define CSDEBUG"//#define CSDEBUG 0#if CSDEBUG#define CSDEBUG_INTERFACE 1#else#undef CSDEBUG_INTERFACE#endif//// cs_debugmask areas//#define CS_INIT	 	0x00000001	// initialization and probe functions#define CS_ERROR 	0x00000002	// tmp debugging bit placeholder#define CS_INTERRUPT	0x00000004	// interrupt handler (separate from all other)#define CS_FUNCTION 	0x00000008	// enter/leave functions#define CS_WAVE_WRITE 	0x00000010	// write information for wave#define CS_WAVE_READ 	0x00000020	// read information for wave#define CS_AC97         0x00000040      // AC97 register access#define CS_DESCR        0x00000080      // descriptor management#define CS_OPEN		0x00000400	// all open functions in the driver#define CS_RELEASE	0x00000800	// all release functions in the driver#define CS_PARMS	0x00001000	// functional and operational parameters#define CS_IOCTL	0x00002000	// ioctl (non-mixer)#define CS_TMP		0x10000000	// tmp debug mask bit//// CSDEBUG is usual mode is set to 1, then use the// cs_debuglevel and cs_debugmask to turn on or off debugging.// Debug level of 1 has been defined to be kernel errors and info// that should be printed on any released driver.//#if CSDEBUG#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask) ) {x;}#else#define CS_DBGOUT(mask,level,x)#endif#if CSDEBUGstatic unsigned long cs_debuglevel = 4;	// levels range from 1-9static unsigned long cs_debugmask = CS_INIT /*| CS_IOCTL*/;MODULE_PARM(cs_debuglevel, "i");MODULE_PARM(cs_debugmask, "i");#endif#define CS_TRUE 	1#define CS_FALSE 	0#define CS_TYPE_ADC 0#define CS_TYPE_DAC 1#define SER_BASE    (A_SER_BASE_1 + KSEG1)#define SS_CSR(t)   (SER_BASE+t)#define SS_TXTBL(t) (SER_BASE+R_SER_TX_TABLE_BASE+(t*8))#define SS_RXTBL(t) (SER_BASE+R_SER_RX_TABLE_BASE+(t*8))#define FRAME_BYTES            32#define FRAME_SAMPLE_BYTES      4/* Should this be variable? */#define SAMPLE_BUF_SIZE        (16*1024)#define SAMPLE_FRAME_COUNT     (SAMPLE_BUF_SIZE / FRAME_SAMPLE_BYTES)/* The driver can explode/shrink the frames to/from a smaller sample   buffer */#define DMA_BLOAT_FACTOR       1#define DMA_DESCR              (SAMPLE_FRAME_COUNT / DMA_BLOAT_FACTOR)#define DMA_BUF_SIZE           (DMA_DESCR * FRAME_BYTES)/* Use the maxmium count (255 == 5.1 ms between interrupts) */#define DMA_INT_CNT            ((1 << S_DMA_INT_PKTCNT) - 1)/* Figure this out: how many TX DMAs ahead to schedule a reg access */#define REG_LATENCY            150#define FRAME_TX_US             20#define SERDMA_NEXTBUF(d,f) (((d)->f+1) % (d)->ringsz)static const char invalid_magic[] =    KERN_CRIT "cs4297a: invalid magic value\n";#define VALIDATE_STATE(s)                          \({                                                 \        if (!(s) || (s)->magic != CS4297a_MAGIC) { \                printk(invalid_magic);             \                return -ENXIO;                     \        }                                          \})struct list_head cs4297a_devs = { &cs4297a_devs, &cs4297a_devs };typedef struct serdma_descr_s {        u64 descr_a;        u64 descr_b;} serdma_descr_t;typedef unsigned long paddr_t;typedef struct serdma_s {        unsigned         ringsz;        serdma_descr_t  *descrtab;        serdma_descr_t  *descrtab_end;        paddr_t          descrtab_phys;                serdma_descr_t  *descr_add;        serdma_descr_t  *descr_rem;                u64  *dma_buf;           // buffer for DMA contents (frames)        paddr_t          dma_buf_phys;        u16  *sample_buf;		// tmp buffer for sample conversions        u16  *sb_swptr;        u16  *sb_hwptr;        u16  *sb_end;        dma_addr_t dmaaddr;//        unsigned buforder;	// Log base 2 of 'dma_buf' size in bytes..        unsigned numfrag;	// # of 'fragments' in the buffer.        unsigned fragshift;	// Log base 2 of fragment size.        unsigned hwptr, swptr;        unsigned total_bytes;	// # bytes process since open.        unsigned blocks;	// last returned blocks value GETOPTR        unsigned wakeup;	// interrupt occurred on block         int count;        unsigned underrun;	// underrun flag        unsigned error;	// over/underrun         wait_queue_head_t wait;        wait_queue_head_t reg_wait;        // redundant, but makes calculations easier         unsigned fragsize;	// 2**fragshift..        unsigned sbufsz;	// 2**buforder.        unsigned fragsamples;        // OSS stuff         unsigned mapped:1;	// Buffer mapped in cs4297a_mmap()?        unsigned ready:1;	// prog_dmabuf_dac()/adc() successful?        unsigned endcleared:1;        unsigned type:1;	// adc or dac buffer (CS_TYPE_XXX)        unsigned ossfragshift;        int ossmaxfrags;        unsigned subdivision;} serdma_t;struct cs4297a_state {	// magic 	unsigned int magic;	struct list_head list;	// soundcore stuff 	int dev_audio;	int dev_mixer;	// hardware resources 	unsigned int irq;        struct {                unsigned int rx_ovrrn; /* FIFO */                unsigned int rx_overflow; /* staging buffer */                unsigned int tx_underrun;                unsigned int rx_bad;                unsigned int rx_good;        } stats;	// mixer registers 	struct {		unsigned short vol[10];		unsigned int recsrc;		unsigned int modcnt;		unsigned short micpreamp;	} mix;	// wave stuff   	struct properties {		unsigned fmt;		unsigned fmt_original;	// original requested format		unsigned channels;		unsigned rate;	} prop_dac, prop_adc;	unsigned conversion:1;	// conversion from 16 to 8 bit in progress	unsigned ena;	spinlock_t lock;	struct semaphore open_sem;	struct semaphore open_sem_adc;	struct semaphore open_sem_dac;	mode_t open_mode;	wait_queue_head_t open_wait;	wait_queue_head_t open_wait_adc;	wait_queue_head_t open_wait_dac;	dma_addr_t dmaaddr_sample_buf;	unsigned buforder_sample_buf;	// Log base 2 of 'dma_buf' size in bytes..        serdma_t dma_dac, dma_adc;        volatile u16 read_value;        volatile u16 read_reg;        volatile u64 reg_request;};#if 1#define prog_codec(a,b)#define dealloc_dmabuf(a,b);#endifstatic int prog_dmabuf_adc(struct cs4297a_state *s){	s->dma_adc.ready = 1;	return 0;}static int prog_dmabuf_dac(struct cs4297a_state *s){	s->dma_dac.ready = 1;	return 0;}static void clear_advance(void *buf, unsigned bsize, unsigned bptr,			  unsigned len, unsigned char c){	if (bptr + len > bsize) {		unsigned x = bsize - bptr;		memset(((char *) buf) + bptr, c, x);		bptr = 0;		len -= x;	}	CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO		"cs4297a: clear_advance(): memset %d at 0x%.8x for %d size \n",			(unsigned)c, (unsigned)((char *) buf) + bptr, len));	memset(((char *) buf) + bptr, c, len);}#if CSDEBUG// DEBUG ROUTINES#define SOUND_MIXER_CS_GETDBGLEVEL 	_SIOWR('M',120, int)#define SOUND_MIXER_CS_SETDBGLEVEL 	_SIOWR('M',121, int)#define SOUND_MIXER_CS_GETDBGMASK 	_SIOWR('M',122, int)#define SOUND_MIXER_CS_SETDBGMASK 	_SIOWR('M',123, int)static void cs_printioctl(unsigned int x){	unsigned int i;	unsigned char vidx;	// Index of mixtable1[] member is Device ID 	// and must be <= SOUND_MIXER_NRDEVICES.	// Value of array member is index into s->mix.vol[]	static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = {		[SOUND_MIXER_PCM] = 1,	// voice 		[SOUND_MIXER_LINE1] = 2,	// AUX		[SOUND_MIXER_CD] = 3,	// CD 		[SOUND_MIXER_LINE] = 4,	// Line 		[SOUND_MIXER_SYNTH] = 5,	// FM		[SOUND_MIXER_MIC] = 6,	// Mic 		[SOUND_MIXER_SPEAKER] = 7,	// Speaker 		[SOUND_MIXER_RECLEV] = 8,	// Recording level 		[SOUND_MIXER_VOLUME] = 9	// Master Volume 	};	switch (x) {	case SOUND_MIXER_CS_GETDBGMASK:		CS_DBGOUT(CS_IOCTL, 4,			  printk("SOUND_MIXER_CS_GETDBGMASK:\n"));		break;	case SOUND_MIXER_CS_GETDBGLEVEL:		CS_DBGOUT(CS_IOCTL, 4,			  printk("SOUND_MIXER_CS_GETDBGLEVEL:\n"));		break;	case SOUND_MIXER_CS_SETDBGMASK:		CS_DBGOUT(CS_IOCTL, 4,			  printk("SOUND_MIXER_CS_SETDBGMASK:\n"));		break;	case SOUND_MIXER_CS_SETDBGLEVEL:		CS_DBGOUT(CS_IOCTL, 4,			  printk("SOUND_MIXER_CS_SETDBGLEVEL:\n"));		break;	case OSS_GETVERSION:		CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION:\n"));		break;	case SNDCTL_DSP_SYNC:		CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC:\n"));		break;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?