📄 intel8x0.c
字号:
/* * ALSA driver for Intel ICH (i8x0) chipsets * * Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz> * * * This code also contains alpha support for SiS 735 chipsets provided * by Mike Pieper <mptei@users.sourceforge.net>. We have no datasheet * for SiS735, so the code is not fully functional. * * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include <sound/driver.h>#include <asm/io.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/moduleparam.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/ac97_codec.h>#include <sound/info.h>#include <sound/initval.h>/* for 440MX workaround */#include <asm/pgtable.h>#include <asm/cacheflush.h>MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{Intel,82801AA-ICH}," "{Intel,82901AB-ICH0}," "{Intel,82801BA-ICH2}," "{Intel,82801CA-ICH3}," "{Intel,82801DB-ICH4}," "{Intel,ICH5}," "{Intel,ICH6}," "{Intel,ICH7}," "{Intel,6300ESB}," "{Intel,ESB2}," "{Intel,MX440}," "{SiS,SI7012}," "{NVidia,nForce Audio}," "{NVidia,nForce2 Audio}," "{AMD,AMD768}," "{AMD,AMD8111}," "{ALI,M5455}}");static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */static int ac97_clock;static char *ac97_quirk;static int buggy_semaphore;static int buggy_irq = -1; /* auto-check */static int xbox;static int spdif_aclink = -1;module_param(index, int, 0444);MODULE_PARM_DESC(index, "Index value for Intel i8x0 soundcard.");module_param(id, charp, 0444);MODULE_PARM_DESC(id, "ID string for Intel i8x0 soundcard.");module_param(ac97_clock, int, 0444);MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = auto-detect).");module_param(ac97_quirk, charp, 0444);MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");module_param(buggy_semaphore, bool, 0444);MODULE_PARM_DESC(buggy_semaphore, "Enable workaround for hardwares with problematic codec semaphores.");module_param(buggy_irq, bool, 0444);MODULE_PARM_DESC(buggy_irq, "Enable workaround for buggy interrupts on some motherboards.");module_param(xbox, bool, 0444);MODULE_PARM_DESC(xbox, "Set to 1 for Xbox, if you have problems with the AC'97 codec detection.");module_param(spdif_aclink, int, 0444);MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link.");/* just for backward compatibility */static int enable;module_param(enable, bool, 0444);static int joystick;module_param(joystick, int, 0444);/* * Direct registers */enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI, DEVICE_NFORCE };#define ICHREG(x) ICH_REG_##x#define DEFINE_REGSET(name,base) \enum { \ ICH_REG_##name##_BDBAR = base + 0x0, /* dword - buffer descriptor list base address */ \ ICH_REG_##name##_CIV = base + 0x04, /* byte - current index value */ \ ICH_REG_##name##_LVI = base + 0x05, /* byte - last valid index */ \ ICH_REG_##name##_SR = base + 0x06, /* byte - status register */ \ ICH_REG_##name##_PICB = base + 0x08, /* word - position in current buffer */ \ ICH_REG_##name##_PIV = base + 0x0a, /* byte - prefetched index value */ \ ICH_REG_##name##_CR = base + 0x0b, /* byte - control register */ \};/* busmaster blocks */DEFINE_REGSET(OFF, 0); /* offset */DEFINE_REGSET(PI, 0x00); /* PCM in */DEFINE_REGSET(PO, 0x10); /* PCM out */DEFINE_REGSET(MC, 0x20); /* Mic in *//* ICH4 busmaster blocks */DEFINE_REGSET(MC2, 0x40); /* Mic in 2 */DEFINE_REGSET(PI2, 0x50); /* PCM in 2 */DEFINE_REGSET(SP, 0x60); /* SPDIF out *//* values for each busmaster block *//* LVI */#define ICH_REG_LVI_MASK 0x1f/* SR */#define ICH_FIFOE 0x10 /* FIFO error */#define ICH_BCIS 0x08 /* buffer completion interrupt status */#define ICH_LVBCI 0x04 /* last valid buffer completion interrupt */#define ICH_CELV 0x02 /* current equals last valid */#define ICH_DCH 0x01 /* DMA controller halted *//* PIV */#define ICH_REG_PIV_MASK 0x1f /* mask *//* CR */#define ICH_IOCE 0x10 /* interrupt on completion enable */#define ICH_FEIE 0x08 /* fifo error interrupt enable */#define ICH_LVBIE 0x04 /* last valid buffer interrupt enable */#define ICH_RESETREGS 0x02 /* reset busmaster registers */#define ICH_STARTBM 0x01 /* start busmaster operation *//* global block */#define ICH_REG_GLOB_CNT 0x2c /* dword - global control */#define ICH_PCM_SPDIF_MASK 0xc0000000 /* s/pdif pcm slot mask (ICH4) */#define ICH_PCM_SPDIF_NONE 0x00000000 /* reserved - undefined */#define ICH_PCM_SPDIF_78 0x40000000 /* s/pdif pcm on slots 7&8 */#define ICH_PCM_SPDIF_69 0x80000000 /* s/pdif pcm on slots 6&9 */#define ICH_PCM_SPDIF_1011 0xc0000000 /* s/pdif pcm on slots 10&11 */#define ICH_PCM_20BIT 0x00400000 /* 20-bit samples (ICH4) */#define ICH_PCM_246_MASK 0x00300000 /* 6 channels (not all chips) */#define ICH_PCM_6 0x00200000 /* 6 channels (not all chips) */#define ICH_PCM_4 0x00100000 /* 4 channels (not all chips) */#define ICH_PCM_2 0x00000000 /* 2 channels (stereo) */#define ICH_SIS_PCM_246_MASK 0x000000c0 /* 6 channels (SIS7012) */#define ICH_SIS_PCM_6 0x00000080 /* 6 channels (SIS7012) */#define ICH_SIS_PCM_4 0x00000040 /* 4 channels (SIS7012) */#define ICH_SIS_PCM_2 0x00000000 /* 2 channels (SIS7012) */#define ICH_TRIE 0x00000040 /* tertiary resume interrupt enable */#define ICH_SRIE 0x00000020 /* secondary resume interrupt enable */#define ICH_PRIE 0x00000010 /* primary resume interrupt enable */#define ICH_ACLINK 0x00000008 /* AClink shut off */#define ICH_AC97WARM 0x00000004 /* AC'97 warm reset */#define ICH_AC97COLD 0x00000002 /* AC'97 cold reset */#define ICH_GIE 0x00000001 /* GPI interrupt enable */#define ICH_REG_GLOB_STA 0x30 /* dword - global status */#define ICH_TRI 0x20000000 /* ICH4: tertiary (AC_SDIN2) resume interrupt */#define ICH_TCR 0x10000000 /* ICH4: tertiary (AC_SDIN2) codec ready */#define ICH_BCS 0x08000000 /* ICH4: bit clock stopped */#define ICH_SPINT 0x04000000 /* ICH4: S/PDIF interrupt */#define ICH_P2INT 0x02000000 /* ICH4: PCM2-In interrupt */#define ICH_M2INT 0x01000000 /* ICH4: Mic2-In interrupt */#define ICH_SAMPLE_CAP 0x00c00000 /* ICH4: sample capability bits (RO) */#define ICH_SAMPLE_16_20 0x00400000 /* ICH4: 16- and 20-bit samples */#define ICH_MULTICHAN_CAP 0x00300000 /* ICH4: multi-channel capability bits (RO) */#define ICH_SIS_TRI 0x00080000 /* SIS: tertiary resume irq */#define ICH_SIS_TCR 0x00040000 /* SIS: tertiary codec ready */#define ICH_MD3 0x00020000 /* modem power down semaphore */#define ICH_AD3 0x00010000 /* audio power down semaphore */#define ICH_RCS 0x00008000 /* read completion status */#define ICH_BIT3 0x00004000 /* bit 3 slot 12 */#define ICH_BIT2 0x00002000 /* bit 2 slot 12 */#define ICH_BIT1 0x00001000 /* bit 1 slot 12 */#define ICH_SRI 0x00000800 /* secondary (AC_SDIN1) resume interrupt */#define ICH_PRI 0x00000400 /* primary (AC_SDIN0) resume interrupt */#define ICH_SCR 0x00000200 /* secondary (AC_SDIN1) codec ready */#define ICH_PCR 0x00000100 /* primary (AC_SDIN0) codec ready */#define ICH_MCINT 0x00000080 /* MIC capture interrupt */#define ICH_POINT 0x00000040 /* playback interrupt */#define ICH_PIINT 0x00000020 /* capture interrupt */#define ICH_NVSPINT 0x00000010 /* nforce spdif interrupt */#define ICH_MOINT 0x00000004 /* modem playback interrupt */#define ICH_MIINT 0x00000002 /* modem capture interrupt */#define ICH_GSCI 0x00000001 /* GPI status change interrupt */#define ICH_REG_ACC_SEMA 0x34 /* byte - codec write semaphore */#define ICH_CAS 0x01 /* codec access semaphore */#define ICH_REG_SDM 0x80#define ICH_DI2L_MASK 0x000000c0 /* PCM In 2, Mic In 2 data in line */#define ICH_DI2L_SHIFT 6#define ICH_DI1L_MASK 0x00000030 /* PCM In 1, Mic In 1 data in line */#define ICH_DI1L_SHIFT 4#define ICH_SE 0x00000008 /* steer enable */#define ICH_LDI_MASK 0x00000003 /* last codec read data input */#define ICH_MAX_FRAGS 32 /* max hw frags *//* * registers for Ali5455 *//* ALi 5455 busmaster blocks */DEFINE_REGSET(AL_PI, 0x40); /* ALi PCM in */DEFINE_REGSET(AL_PO, 0x50); /* Ali PCM out */DEFINE_REGSET(AL_MC, 0x60); /* Ali Mic in */DEFINE_REGSET(AL_CDC_SPO, 0x70); /* Ali Codec SPDIF out */DEFINE_REGSET(AL_CENTER, 0x80); /* Ali center out */DEFINE_REGSET(AL_LFE, 0x90); /* Ali center out */DEFINE_REGSET(AL_CLR_SPI, 0xa0); /* Ali Controller SPDIF in */DEFINE_REGSET(AL_CLR_SPO, 0xb0); /* Ali Controller SPDIF out */DEFINE_REGSET(AL_I2S, 0xc0); /* Ali I2S in */DEFINE_REGSET(AL_PI2, 0xd0); /* Ali PCM2 in */DEFINE_REGSET(AL_MC2, 0xe0); /* Ali Mic2 in */enum { ICH_REG_ALI_SCR = 0x00, /* System Control Register */ ICH_REG_ALI_SSR = 0x04, /* System Status Register */ ICH_REG_ALI_DMACR = 0x08, /* DMA Control Register */ ICH_REG_ALI_FIFOCR1 = 0x0c, /* FIFO Control Register 1 */ ICH_REG_ALI_INTERFACECR = 0x10, /* Interface Control Register */ ICH_REG_ALI_INTERRUPTCR = 0x14, /* Interrupt control Register */ ICH_REG_ALI_INTERRUPTSR = 0x18, /* Interrupt Status Register */ ICH_REG_ALI_FIFOCR2 = 0x1c, /* FIFO Control Register 2 */ ICH_REG_ALI_CPR = 0x20, /* Command Port Register */ ICH_REG_ALI_CPR_ADDR = 0x22, /* ac97 addr write */ ICH_REG_ALI_SPR = 0x24, /* Status Port Register */ ICH_REG_ALI_SPR_ADDR = 0x26, /* ac97 addr read */ ICH_REG_ALI_FIFOCR3 = 0x2c, /* FIFO Control Register 3 */ ICH_REG_ALI_TTSR = 0x30, /* Transmit Tag Slot Register */ ICH_REG_ALI_RTSR = 0x34, /* Receive Tag Slot Register */ ICH_REG_ALI_CSPSR = 0x38, /* Command/Status Port Status Register */ ICH_REG_ALI_CAS = 0x3c, /* Codec Write Semaphore Register */ ICH_REG_ALI_HWVOL = 0xf0, /* hardware volume control/status */ ICH_REG_ALI_I2SCR = 0xf4, /* I2S control/status */ ICH_REG_ALI_SPDIFCSR = 0xf8, /* spdif channel status register */ ICH_REG_ALI_SPDIFICS = 0xfc, /* spdif interface control/status */};#define ALI_CAS_SEM_BUSY 0x80000000#define ALI_CPR_ADDR_SECONDARY 0x100#define ALI_CPR_ADDR_READ 0x80#define ALI_CSPSR_CODEC_READY 0x08#define ALI_CSPSR_READ_OK 0x02#define ALI_CSPSR_WRITE_OK 0x01/* interrupts for the whole chip by interrupt status register finish */ #define ALI_INT_MICIN2 (1<<26)#define ALI_INT_PCMIN2 (1<<25)#define ALI_INT_I2SIN (1<<24)#define ALI_INT_SPDIFOUT (1<<23) /* controller spdif out INTERRUPT */#define ALI_INT_SPDIFIN (1<<22)#define ALI_INT_LFEOUT (1<<21)#define ALI_INT_CENTEROUT (1<<20)#define ALI_INT_CODECSPDIFOUT (1<<19)#define ALI_INT_MICIN (1<<18)#define ALI_INT_PCMOUT (1<<17)#define ALI_INT_PCMIN (1<<16)#define ALI_INT_CPRAIS (1<<7) /* command port available */#define ALI_INT_SPRAIS (1<<5) /* status port available */#define ALI_INT_GPIO (1<<1)#define ALI_INT_MASK (ALI_INT_SPDIFOUT|ALI_INT_CODECSPDIFOUT|\ ALI_INT_MICIN|ALI_INT_PCMOUT|ALI_INT_PCMIN)#define ICH_ALI_SC_RESET (1<<31) /* master reset */#define ICH_ALI_SC_AC97_DBL (1<<30)#define ICH_ALI_SC_CODEC_SPDF (3<<20) /* 1=7/8, 2=6/9, 3=10/11 */#define ICH_ALI_SC_IN_BITS (3<<18)#define ICH_ALI_SC_OUT_BITS (3<<16)#define ICH_ALI_SC_6CH_CFG (3<<14)#define ICH_ALI_SC_PCM_4 (1<<8)#define ICH_ALI_SC_PCM_6 (2<<8)#define ICH_ALI_SC_PCM_246_MASK (3<<8)#define ICH_ALI_SS_SEC_ID (3<<5)#define ICH_ALI_SS_PRI_ID (3<<3)#define ICH_ALI_IF_AC97SP (1<<21)#define ICH_ALI_IF_MC (1<<20)#define ICH_ALI_IF_PI (1<<19)#define ICH_ALI_IF_MC2 (1<<18)#define ICH_ALI_IF_PI2 (1<<17)#define ICH_ALI_IF_LINE_SRC (1<<15) /* 0/1 = slot 3/6 */#define ICH_ALI_IF_MIC_SRC (1<<14) /* 0/1 = slot 3/6 */#define ICH_ALI_IF_SPDF_SRC (3<<12) /* 00 = PCM, 01 = AC97-in, 10 = spdif-in, 11 = i2s */#define ICH_ALI_IF_AC97_OUT (3<<8) /* 00 = PCM, 10 = spdif-in, 11 = i2s */#define ICH_ALI_IF_PO_SPDF (1<<3)#define ICH_ALI_IF_PO (1<<1)/* * */enum { ICHD_PCMIN, ICHD_PCMOUT, ICHD_MIC, ICHD_MIC2, ICHD_PCM2IN, ICHD_SPBAR, ICHD_LAST = ICHD_SPBAR};enum { NVD_PCMIN, NVD_PCMOUT, NVD_MIC, NVD_SPBAR, NVD_LAST = NVD_SPBAR};enum { ALID_PCMIN, ALID_PCMOUT, ALID_MIC, ALID_AC97SPDIFOUT, ALID_SPDIFIN, ALID_SPDIFOUT, ALID_LAST = ALID_SPDIFOUT};#define get_ichdev(substream) (substream->runtime->private_data)struct ichdev { unsigned int ichd; /* ich device number */ unsigned long reg_offset; /* offset to bmaddr */ u32 *bdbar; /* CPU address (32bit) */ unsigned int bdbar_addr; /* PCI bus address (32bit) */ struct snd_pcm_substream *substream; unsigned int physbuf; /* physical address (32bit) */ unsigned int size; unsigned int fragsize; unsigned int fragsize1; unsigned int position; unsigned int pos_shift; int frags; int lvi; int lvi_frag; int civ; int ack; int ack_reload; unsigned int ack_bit; unsigned int roff_sr; unsigned int roff_picb; unsigned int int_sta_mask; /* interrupt status mask */ unsigned int ali_slot; /* ALI DMA slot */ struct ac97_pcm *pcm; int pcm_open_flag; unsigned int page_attr_changed: 1; unsigned int suspended: 1;};struct intel8x0 { unsigned int device_type; int irq; void __iomem *addr; void __iomem *bmaddr; struct pci_dev *pci; struct snd_card *card; int pcm_devs; struct snd_pcm *pcm[6]; struct ichdev ichd[6]; unsigned multi4: 1, multi6: 1, dra: 1, smp20bit: 1; unsigned in_ac97_init: 1, in_sdin_init: 1; unsigned in_measurement: 1; /* during ac97 clock measurement */ unsigned fix_nocache: 1; /* workaround for 440MX */ unsigned buggy_irq: 1; /* workaround for buggy mobos */ unsigned xbox: 1; /* workaround for Xbox AC'97 detection */ unsigned buggy_semaphore: 1; /* workaround for buggy codec semaphore */ int spdif_idx; /* SPDIF BAR index; *_SPBAR or -1 if use PCMOUT */ unsigned int sdm_saved; /* SDM reg value */ struct snd_ac97_bus *ac97_bus; struct snd_ac97 *ac97[3]; unsigned int ac97_sdin[3]; unsigned int max_codecs, ncodecs; unsigned int *codec_bit; unsigned int codec_isr_bits; unsigned int codec_ready_bits; spinlock_t reg_lock; u32 bdbars_count; struct snd_dma_buffer bdbars; u32 int_sta_reg; /* interrupt status register */ u32 int_sta_mask; /* interrupt status mask */};static struct pci_device_id snd_intel8x0_ids[] = { { 0x8086, 0x2415, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801AA */ { 0x8086, 0x2425, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82901AB */ { 0x8086, 0x2445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 82801BA */ { 0x8086, 0x2485, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH3 */ { 0x8086, 0x24c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH4 */ { 0x8086, 0x24d5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH5 */ { 0x8086, 0x25a6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ESB */ { 0x8086, 0x266e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH6 */ { 0x8086, 0x27de, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ICH7 */ { 0x8086, 0x2698, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* ESB2 */ { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */ { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7012 */ { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE */ { 0x10de, 0x003a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* MCP04 */ { 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE2 */ { 0x10de, 0x0059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK804 */ { 0x10de, 0x008a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK8 */ { 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE3 */ { 0x10de, 0x00ea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK8S */ { 0x10de, 0x026b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* MCP51 */ { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */ { 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD768 */ { 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI }, /* Ali5455 */ { 0, }};MODULE_DEVICE_TABLE(pci, snd_intel8x0_ids);/* * Lowlevel I/O - busmaster */static inline u8 igetbyte(struct intel8x0 *chip, u32 offset){ return ioread8(chip->bmaddr + offset);}static inline u16 igetword(struct intel8x0 *chip, u32 offset){ return ioread16(chip->bmaddr + offset);}static inline u32 igetdword(struct intel8x0 *chip, u32 offset)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -