📄 cs4281m.c
字号:
/********************************************************************************* "cs4281.c" -- Cirrus Logic-Crystal CS4281 linux audio driver.** 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).** 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 cs4281_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.********************************************************************************//* uncomment the following line to disable building PM support into the driver *///#define NOT_CS4281_PM 1 #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/pci.h>#include <linux/bitops.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/poll.h>#include <linux/fs.h>#include <linux/wait.h>#include <asm/current.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/page.h>#include <asm/uaccess.h>//#include "cs_dm.h"#include "cs4281_hwdefs.h"#include "cs4281pm.h"struct cs4281_state;static void stop_dac(struct cs4281_state *s);static void stop_adc(struct cs4281_state *s);static void start_dac(struct cs4281_state *s);static void start_adc(struct cs4281_state *s);#undef OSS_DOCUMENTED_MIXER_SEMANTICS// --------------------------------------------------------------------- #ifndef PCI_VENDOR_ID_CIRRUS#define PCI_VENDOR_ID_CIRRUS 0x1013#endif#ifndef PCI_DEVICE_ID_CRYSTAL_CS4281#define PCI_DEVICE_ID_CRYSTAL_CS4281 0x6005#endif#define CS4281_MAGIC ((PCI_DEVICE_ID_CRYSTAL_CS4281<<16) | PCI_VENDOR_ID_CIRRUS)#define CS4281_CFLR_DEFAULT 0x00000001 /* CFLR must be in AC97 link mode */// 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_param(defaultorder, ulong, 0);//// Turn on/off debugging compilation by commenting out "#define CSDEBUG"//#define CSDEBUG 1#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_MIDI_WRITE 0x00000040 // write information for midi#define CS_MIDI_READ 0x00000080 // read information for midi#define CS_MPU401_WRITE 0x00000100 // write information for mpu401#define CS_MPU401_READ 0x00000200 // read information for mpu401#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_PM 0x00004000 // power management #define CS_TMP 0x10000000 // tmp debug mask bit#define CS_IOCTL_CMD_SUSPEND 0x1 // suspend#define CS_IOCTL_CMD_RESUME 0x2 // resume//// 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 = 1; // levels range from 1-9static unsigned long cs_debugmask = CS_INIT | CS_ERROR; // use CS_DBGOUT with various mask valuesmodule_param(cs_debuglevel, ulong, 0);module_param(cs_debugmask, ulong, 0);#endif#define CS_TRUE 1#define CS_FALSE 0// MIDI buffer sizes #define MIDIINBUF 500#define MIDIOUTBUF 500#define FMODE_MIDI_SHIFT 3#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT)#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)#define CS4281_MAJOR_VERSION 1#define CS4281_MINOR_VERSION 13#ifdef __ia64__#define CS4281_ARCH 64 //architecture key#else#define CS4281_ARCH 32 //architecture key#endif#define CS_TYPE_ADC 0#define CS_TYPE_DAC 1static const char invalid_magic[] = KERN_CRIT "cs4281: invalid magic value\n";#define VALIDATE_STATE(s) \({ \ if (!(s) || (s)->magic != CS4281_MAGIC) { \ printk(invalid_magic); \ return -ENXIO; \ } \})//LIST_HEAD(cs4281_devs);static struct list_head cs4281_devs = { &cs4281_devs, &cs4281_devs };struct cs4281_state; #include "cs4281_wrapper-24.c"struct cs4281_state { // magic unsigned int magic; // we keep the cards in a linked list struct cs4281_state *next; // pcidev is needed to turn off the DDMA controller at driver shutdown struct pci_dev *pcidev; struct list_head list; // soundcore stuff int dev_audio; int dev_mixer; int dev_midi; // hardware resources unsigned int pBA0phys, pBA1phys; char __iomem *pBA0; char __iomem *pBA1; unsigned int irq; // 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; unsigned char clkdiv; } prop_dac, prop_adc; unsigned conversion:1; // conversion from 16 to 8 bit in progress void *tmpbuff; // tmp buffer for sample conversions 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_tmpbuff; unsigned buforder_tmpbuff; // Log base 2 of 'rawbuf' size in bytes.. struct dmabuf { void *rawbuf; // Physical address of dma_addr_t dmaaddr; unsigned buforder; // Log base 2 of 'rawbuf' 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; // redundant, but makes calculations easier unsigned fragsize; // 2**fragshift.. unsigned dmasize; // 2**buforder. unsigned fragsamples; // OSS stuff unsigned mapped:1; // Buffer mapped in cs4281_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; } dma_dac, dma_adc; // midi stuff struct { unsigned ird, iwr, icnt; unsigned ord, owr, ocnt; wait_queue_head_t iwait; wait_queue_head_t owait; struct timer_list timer; unsigned char ibuf[MIDIINBUF]; unsigned char obuf[MIDIOUTBUF]; } midi; struct cs4281_pm pm; struct cs4281_pipeline pl[CS4281_NUMBER_OF_PIPELINES];};#include <linux/pm_legacy.h>#include "cs4281pm-24.c"#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)#define SOUND_MIXER_CS_APM _SIOWR('M',124, 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; case SNDCTL_DSP_SETDUPLEX: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX:\n")); break; case SNDCTL_DSP_GETCAPS: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS:\n")); break; case SNDCTL_DSP_RESET: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET:\n")); break; case SNDCTL_DSP_SPEED: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED:\n")); break; case SNDCTL_DSP_STEREO: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO:\n")); break; case SNDCTL_DSP_CHANNELS: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS:\n")); break; case SNDCTL_DSP_GETFMTS: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS:\n")); break; case SNDCTL_DSP_SETFMT: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT:\n")); break; case SNDCTL_DSP_POST: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST:\n")); break; case SNDCTL_DSP_GETTRIGGER: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER:\n")); break; case SNDCTL_DSP_SETTRIGGER: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER:\n")); break; case SNDCTL_DSP_GETOSPACE: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE:\n")); break; case SNDCTL_DSP_GETISPACE: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE:\n")); break; case SNDCTL_DSP_NONBLOCK: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK:\n")); break; case SNDCTL_DSP_GETODELAY: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY:\n")); break; case SNDCTL_DSP_GETIPTR: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR:\n")); break; case SNDCTL_DSP_GETOPTR: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR:\n")); break; case SNDCTL_DSP_GETBLKSIZE: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE:\n")); break; case SNDCTL_DSP_SETFRAGMENT: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFRAGMENT:\n")); break; case SNDCTL_DSP_SUBDIVIDE: CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE:\n")); break; case SOUND_PCM_READ_RATE: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE:\n")); break; case SOUND_PCM_READ_CHANNELS: CS_DBGOUT(CS_IOCTL, 4,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -