📄 cs46xx.c
字号:
/* * Crystal SoundFusion CS46xx driver * * Copyright 1998-2001 Cirrus Logic Corporation <pcaudio@crystal.cirrus.com> * <twoller@crystal.cirrus.com> * Copyright 1999-2000 Jaroslav Kysela <perex@suse.cz> * Copyright 2000 Alan Cox <alan@redhat.com> * * The core of this code is taken from the ALSA project driver by * Jaroslav. Please send Jaroslav the credit for the driver and * report bugs in this port to <alan@redhat.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. * Current maintainers: * Cirrus Logic Corporation, Thomas Woller (tw) * <twoller@crystal.cirrus.com> * Nils Faerber (nf) * <nils@kernelconcepts.de> * Thanks to David Pollard for testing. * * Changes: * 20000909-nf Changed cs_read, cs_write and drain_dac * 20001025-tw Separate Playback/Capture structs and buffers. * Added Scatter/Gather support for Playback. * Added Capture. * 20001027-nf Port to kernel 2.4.0-test9, some clean-ups * Start of powermanagement support (CS46XX_PM). * 20001128-tw Add module parm for default buffer order. * added DMA_GFP flag to kmalloc dma buffer allocs. * backfill silence to eliminate stuttering on * underruns. * 20001201-tw add resyncing of swptr on underruns. * 20001205-tw-nf fixed GETOSPACE ioctl() after open() * 20010113-tw patch from Hans Grobler general cleanup. * 20010117-tw 2.4.0 pci cleanup, wrapper code for 2.2.16-2.4.0 * 20010118-tw basic PM support for 2.2.16+ and 2.4.0/2.4.2. * 20010228-dh patch from David Huggins - cs_update_ptr recursion. * 20010409-tw add hercules game theatre XP amp code. * 20010420-tw cleanup powerdown/up code. * 20010521-tw eliminate pops, and fixes for powerdown. * 20010525-tw added fixes for thinkpads with powerdown logic. * 20010723-sh patch from Horms (Simon Horman) - * SOUND_PCM_READ_BITS returns bits as set in driver * rather than a logical or of the possible values. * Various ioctls handle the case where the device * is open for reading or writing but not both better. * * Status: * Playback/Capture supported from 8k-48k. * 16Bit Signed LE & 8Bit Unsigned, with Mono or Stereo supported. * * APM/PM - 2.2.x APM is enabled and functioning fine. APM can also * be enabled for 2.4.x by modifying the CS46XX_ACPI_SUPPORT macro * definition. * * Hercules Game Theatre XP - the EGPIO2 pin controls the external Amp, * so, use the drain/polarity to enable. * hercules_egpio_disable set to 1, will force a 0 to EGPIODR. * * VTB Santa Cruz - the GPIO7/GPIO8 on the Secondary Codec control * the external amplifier for the "back" speakers, since we do not * support the secondary codec then this external amp is also not * turned on. */ #include <linux/list.h>#include <linux/version.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 <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/hardirq.h>#include <linux/ac97_codec.h>#include "cs46xxpm-24.h"#include "cs46xx_wrapper-24.h"#include "cs461x.h"/* MIDI buffer sizes */#define CS_MIDIINBUF 500#define CS_MIDIOUTBUF 500#define ADC_RUNNING 1#define DAC_RUNNING 2#define CS_FMT_16BIT 1 /* These are fixed in fact */#define CS_FMT_STEREO 2#define CS_FMT_MASK 3#define CS_TYPE_ADC 1#define CS_TYPE_DAC 2#define CS_TRUE 1#define CS_FALSE 0#define CS_INC_USE_COUNT(m) (atomic_inc(m))#define CS_DEC_USE_COUNT(m) (atomic_dec(m))#define CS_DEC_AND_TEST(m) (atomic_dec_and_test(m))#define CS_IN_USE(m) (atomic_read(m) != 0)#define CS_DBGBREAKPOINT {__asm__("INT $3");}/* * CS461x definitions */ #define CS461X_BA0_SIZE 0x2000#define CS461X_BA1_DATA0_SIZE 0x3000#define CS461X_BA1_DATA1_SIZE 0x3800#define CS461X_BA1_PRG_SIZE 0x7000#define CS461X_BA1_REG_SIZE 0x0100#define GOF_PER_SEC 200#define CSDEBUG_INTERFACE 1#define CSDEBUG 1/* * Turn on/off debugging compilation by using 1/0 respectively for CSDEBUG * * * 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/* * 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 /* PM */#define CS_TMP 0x10000000 /* tmp debug mask bit */#define CS_IOCTL_CMD_SUSPEND 0x1 // suspend#define CS_IOCTL_CMD_RESUME 0x2 // resume#if CSDEBUGstatic unsigned long cs_debuglevel=1; /* levels range from 1-9 */MODULE_PARM(cs_debuglevel, "i");static unsigned long cs_debugmask=CS_INIT | CS_ERROR; /* use CS_DBGOUT with various mask values */MODULE_PARM(cs_debugmask, "i");#endifstatic unsigned long hercules_egpio_disable=0; /* if non-zero set all EGPIO to 0 */MODULE_PARM(hercules_egpio_disable, "i");static unsigned long initdelay=700; /* PM delay in millisecs */MODULE_PARM(initdelay, "i");static unsigned long powerdown=-1; /* turn on/off powerdown processing in driver */MODULE_PARM(powerdown, "i");#define DMABUF_DEFAULTORDER 3static unsigned long defaultorder=DMABUF_DEFAULTORDER;MODULE_PARM(defaultorder, "i");static int external_amp;MODULE_PARM(external_amp, "i");static int thinkpad;MODULE_PARM(thinkpad, "i");/** set the powerdown module parm to 0 to disable all * powerdown. also set thinkpad to 1 to disable powerdown, * but also to enable the clkrun functionality.*/static unsigned cs_powerdown=1;static unsigned cs_laptop_wait=1;/* An instance of the 4610 channel */struct cs_channel { int used; int num; void *state;};#define CS46XX_MAJOR_VERSION "1"#define CS46XX_MINOR_VERSION "28"#ifdef __ia64__#define CS46XX_ARCH "64" //architecture key#else#define CS46XX_ARCH "32" //architecture key#endifstruct list_head cs46xx_devs = { &cs46xx_devs, &cs46xx_devs };/* magic numbers to protect our data structures */#define CS_CARD_MAGIC 0x43525553 /* "CRUS" */#define CS_STATE_MAGIC 0x4c4f4749 /* "LOGI" */#define NR_HW_CH 3/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */#define NR_AC97 2static const unsigned sample_size[] = { 1, 2, 2, 4 };static const unsigned sample_shift[] = { 0, 1, 1, 2 };/* "software" or virtual channel, an instance of opened /dev/dsp */struct cs_state { unsigned int magic; struct cs_card *card; /* Card info */ /* single open lock mechanism, only used for recording */ struct semaphore open_sem; wait_queue_head_t open_wait; /* file mode */ mode_t open_mode; /* virtual channel number */ int virt; struct dmabuf { /* wave sample stuff */ unsigned int rate; unsigned char fmt, enable; /* hardware channel */ struct cs_channel *channel; int pringbuf; /* Software ring slot */ void *pbuf; /* 4K hardware DMA buffer */ /* OSS buffer management stuff */ void *rawbuf; dma_addr_t dma_handle; unsigned buforder; unsigned numfrag; unsigned fragshift; unsigned divisor; unsigned type; void *tmpbuff; /* tmp buffer for sample conversions */ dma_addr_t dmaaddr; dma_addr_t dmaaddr_tmpbuff; unsigned buforder_tmpbuff; /* Log base 2 of size in bytes.. */ /* our buffer acts like a circular ring */ unsigned hwptr; /* where dma last started, updated by update_ptr */ unsigned swptr; /* where driver last clear/filled, updated by read/write */ int count; /* bytes to be comsumed or been generated by dma machine */ unsigned total_bytes; /* total bytes dmaed by hardware */ unsigned blocks; /* total blocks */ unsigned error; /* number of over/underruns */ unsigned underrun; /* underrun pending before next write has occurred */ wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ /* redundant, but makes calculations easier */ unsigned fragsize; unsigned dmasize; unsigned fragsamples; /* OSS stuff */ unsigned mapped:1; unsigned ready:1; unsigned endcleared:1; unsigned SGok:1; unsigned update_flag; unsigned ossfragshift; int ossmaxfrags; unsigned subdivision; } dmabuf; /* Guard against mmap/write/read races */ struct semaphore sem;};struct cs_card { struct cs_channel channel[2]; unsigned int magic; /* We keep cs461x cards in a linked list */ struct cs_card *next; /* The cs461x has a certain amount of cross channel interaction so we use a single per card lock */ spinlock_t lock; /* mixer use count */ atomic_t mixer_use_cnt; /* PCI device stuff */ struct pci_dev * pci_dev; struct list_head list; unsigned int pctl, cctl; /* Hardware DMA flag sets */ /* soundcore stuff */ int dev_audio; int dev_midi; /* structures for abstraction of hardware facilities, codecs, banks and channels*/ struct ac97_codec *ac97_codec[NR_AC97]; struct cs_state *states[2]; u16 ac97_features; int amplifier; /* Amplifier control */ void (*amplifier_ctrl)(struct cs_card *, int); void (*amp_init)(struct cs_card *); int active; /* Active clocking */ void (*active_ctrl)(struct cs_card *, int); /* hardware resources */ unsigned long ba0_addr; unsigned long ba1_addr; u32 irq; /* mappings */ void *ba0; union { struct { u8 *data0; u8 *data1; u8 *pmem; u8 *reg; } name; u8 *idx[4]; } ba1; /* Function support */ struct cs_channel *(*alloc_pcm_channel)(struct cs_card *); struct cs_channel *(*alloc_rec_pcm_channel)(struct cs_card *); void (*free_pcm_channel)(struct cs_card *, int chan); /* /dev/midi stuff */ struct { unsigned ird, iwr, icnt; unsigned ord, owr, ocnt; wait_queue_head_t open_wait; wait_queue_head_t iwait; wait_queue_head_t owait; spinlock_t lock; unsigned char ibuf[CS_MIDIINBUF]; unsigned char obuf[CS_MIDIOUTBUF]; mode_t open_mode; struct semaphore open_sem; } midi; struct cs46xx_pm pm;};static int cs_open_mixdev(struct inode *inode, struct file *file);static int cs_release_mixdev(struct inode *inode, struct file *file);static int cs_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static int cs_hardware_init(struct cs_card *card);static int cs46xx_powerup(struct cs_card *card, unsigned int type);static int cs461x_powerdown(struct cs_card *card, unsigned int type, int suspendflag);static void cs461x_clear_serial_FIFOs(struct cs_card *card, int type);static int cs46xx_suspend_tbl(struct pci_dev *pcidev, u32 state);static int cs46xx_resume_tbl(struct pci_dev *pcidev);static inline unsigned ld2(unsigned int x){ unsigned r = 0; if (x >= 0x10000) { x >>= 16; r += 16; } if (x >= 0x100) { x >>= 8; r += 8; } if (x >= 0x10) { x >>= 4; r += 4; } if (x >= 4) { x >>= 2; r += 2; } if (x >= 2) r++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -