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 + -
显示快捷键?