11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * ALSA driver for Intel ICH (i8x0) chipsets
41da177e4SLinus Torvalds *
5c1017a4cSJaroslav Kysela * Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * This code also contains alpha support for SiS 735 chipsets provided
81da177e4SLinus Torvalds * by Mike Pieper <mptei@users.sourceforge.net>. We have no datasheet
91da177e4SLinus Torvalds * for SiS735, so the code is not fully functional.
101da177e4SLinus Torvalds *
111da177e4SLinus Torvalds
121da177e4SLinus Torvalds */
131da177e4SLinus Torvalds
146cbbfe1cSTakashi Iwai #include <linux/io.h>
151da177e4SLinus Torvalds #include <linux/delay.h>
161da177e4SLinus Torvalds #include <linux/interrupt.h>
171da177e4SLinus Torvalds #include <linux/init.h>
181da177e4SLinus Torvalds #include <linux/pci.h>
191da177e4SLinus Torvalds #include <linux/slab.h>
2065a77217SPaul Gortmaker #include <linux/module.h>
211da177e4SLinus Torvalds #include <sound/core.h>
221da177e4SLinus Torvalds #include <sound/pcm.h>
231da177e4SLinus Torvalds #include <sound/ac97_codec.h>
241da177e4SLinus Torvalds #include <sound/info.h>
251da177e4SLinus Torvalds #include <sound/initval.h>
261da177e4SLinus Torvalds
27c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
281da177e4SLinus Torvalds MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455");
291da177e4SLinus Torvalds MODULE_LICENSE("GPL");
301da177e4SLinus Torvalds
31b7fe4622SClemens Ladisch static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
32b7fe4622SClemens Ladisch static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
336581f4e7STakashi Iwai static int ac97_clock;
34b7fe4622SClemens Ladisch static char *ac97_quirk;
35a67ff6a5SRusty Russell static bool buggy_semaphore;
36beef08a5STakashi Iwai static int buggy_irq = -1; /* auto-check */
37a67ff6a5SRusty Russell static bool xbox;
38a9e99660STakashi Iwai static int spdif_aclink = -1;
39228cf793SKonstantin Ozerkov static int inside_vm = -1;
401da177e4SLinus Torvalds
41b7fe4622SClemens Ladisch module_param(index, int, 0444);
421da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for Intel i8x0 soundcard.");
43b7fe4622SClemens Ladisch module_param(id, charp, 0444);
441da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for Intel i8x0 soundcard.");
45b7fe4622SClemens Ladisch module_param(ac97_clock, int, 0444);
467ce78fc8STakashi Iwai MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = allowlist + auto-detect, 1 = force autodetect).");
47b7fe4622SClemens Ladisch module_param(ac97_quirk, charp, 0444);
481da177e4SLinus Torvalds MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
49b7fe4622SClemens Ladisch module_param(buggy_semaphore, bool, 0444);
50a06147d2STakashi Iwai MODULE_PARM_DESC(buggy_semaphore, "Enable workaround for hardwares with problematic codec semaphores.");
5169116f27SRusty Russell module_param(buggy_irq, bint, 0444);
521da177e4SLinus Torvalds MODULE_PARM_DESC(buggy_irq, "Enable workaround for buggy interrupts on some motherboards.");
53b7fe4622SClemens Ladisch module_param(xbox, bool, 0444);
541da177e4SLinus Torvalds MODULE_PARM_DESC(xbox, "Set to 1 for Xbox, if you have problems with the AC'97 codec detection.");
55a9e99660STakashi Iwai module_param(spdif_aclink, int, 0444);
56a9e99660STakashi Iwai MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link.");
5769116f27SRusty Russell module_param(inside_vm, bint, 0444);
58228cf793SKonstantin Ozerkov MODULE_PARM_DESC(inside_vm, "KVM/Parallels optimization.");
591da177e4SLinus Torvalds
602b3e584bSTakashi Iwai /* just for backward compatibility */
61a67ff6a5SRusty Russell static bool enable;
62698444f3STakashi Iwai module_param(enable, bool, 0444);
632b3e584bSTakashi Iwai static int joystick;
642b3e584bSTakashi Iwai module_param(joystick, int, 0444);
652b3e584bSTakashi Iwai
661da177e4SLinus Torvalds /*
671da177e4SLinus Torvalds * Direct registers
681da177e4SLinus Torvalds */
691da177e4SLinus Torvalds enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI, DEVICE_NFORCE };
701da177e4SLinus Torvalds
711da177e4SLinus Torvalds #define ICHREG(x) ICH_REG_##x
721da177e4SLinus Torvalds
731da177e4SLinus Torvalds #define DEFINE_REGSET(name,base) \
741da177e4SLinus Torvalds enum { \
751da177e4SLinus Torvalds ICH_REG_##name##_BDBAR = base + 0x0, /* dword - buffer descriptor list base address */ \
761da177e4SLinus Torvalds ICH_REG_##name##_CIV = base + 0x04, /* byte - current index value */ \
771da177e4SLinus Torvalds ICH_REG_##name##_LVI = base + 0x05, /* byte - last valid index */ \
781da177e4SLinus Torvalds ICH_REG_##name##_SR = base + 0x06, /* byte - status register */ \
791da177e4SLinus Torvalds ICH_REG_##name##_PICB = base + 0x08, /* word - position in current buffer */ \
801da177e4SLinus Torvalds ICH_REG_##name##_PIV = base + 0x0a, /* byte - prefetched index value */ \
811da177e4SLinus Torvalds ICH_REG_##name##_CR = base + 0x0b, /* byte - control register */ \
82d0f5137bSHuilong Deng }
831da177e4SLinus Torvalds
841da177e4SLinus Torvalds /* busmaster blocks */
851da177e4SLinus Torvalds DEFINE_REGSET(OFF, 0); /* offset */
861da177e4SLinus Torvalds DEFINE_REGSET(PI, 0x00); /* PCM in */
871da177e4SLinus Torvalds DEFINE_REGSET(PO, 0x10); /* PCM out */
881da177e4SLinus Torvalds DEFINE_REGSET(MC, 0x20); /* Mic in */
891da177e4SLinus Torvalds
901da177e4SLinus Torvalds /* ICH4 busmaster blocks */
911da177e4SLinus Torvalds DEFINE_REGSET(MC2, 0x40); /* Mic in 2 */
921da177e4SLinus Torvalds DEFINE_REGSET(PI2, 0x50); /* PCM in 2 */
931da177e4SLinus Torvalds DEFINE_REGSET(SP, 0x60); /* SPDIF out */
941da177e4SLinus Torvalds
951da177e4SLinus Torvalds /* values for each busmaster block */
961da177e4SLinus Torvalds
971da177e4SLinus Torvalds /* LVI */
981da177e4SLinus Torvalds #define ICH_REG_LVI_MASK 0x1f
991da177e4SLinus Torvalds
1001da177e4SLinus Torvalds /* SR */
1011da177e4SLinus Torvalds #define ICH_FIFOE 0x10 /* FIFO error */
1021da177e4SLinus Torvalds #define ICH_BCIS 0x08 /* buffer completion interrupt status */
1031da177e4SLinus Torvalds #define ICH_LVBCI 0x04 /* last valid buffer completion interrupt */
1041da177e4SLinus Torvalds #define ICH_CELV 0x02 /* current equals last valid */
1051da177e4SLinus Torvalds #define ICH_DCH 0x01 /* DMA controller halted */
1061da177e4SLinus Torvalds
1071da177e4SLinus Torvalds /* PIV */
1081da177e4SLinus Torvalds #define ICH_REG_PIV_MASK 0x1f /* mask */
1091da177e4SLinus Torvalds
1101da177e4SLinus Torvalds /* CR */
1111da177e4SLinus Torvalds #define ICH_IOCE 0x10 /* interrupt on completion enable */
1121da177e4SLinus Torvalds #define ICH_FEIE 0x08 /* fifo error interrupt enable */
1131da177e4SLinus Torvalds #define ICH_LVBIE 0x04 /* last valid buffer interrupt enable */
1141da177e4SLinus Torvalds #define ICH_RESETREGS 0x02 /* reset busmaster registers */
1151da177e4SLinus Torvalds #define ICH_STARTBM 0x01 /* start busmaster operation */
1161da177e4SLinus Torvalds
1171da177e4SLinus Torvalds
1181da177e4SLinus Torvalds /* global block */
1191da177e4SLinus Torvalds #define ICH_REG_GLOB_CNT 0x2c /* dword - global control */
1201da177e4SLinus Torvalds #define ICH_PCM_SPDIF_MASK 0xc0000000 /* s/pdif pcm slot mask (ICH4) */
1211da177e4SLinus Torvalds #define ICH_PCM_SPDIF_NONE 0x00000000 /* reserved - undefined */
1221da177e4SLinus Torvalds #define ICH_PCM_SPDIF_78 0x40000000 /* s/pdif pcm on slots 7&8 */
1231da177e4SLinus Torvalds #define ICH_PCM_SPDIF_69 0x80000000 /* s/pdif pcm on slots 6&9 */
1241da177e4SLinus Torvalds #define ICH_PCM_SPDIF_1011 0xc0000000 /* s/pdif pcm on slots 10&11 */
1251da177e4SLinus Torvalds #define ICH_PCM_20BIT 0x00400000 /* 20-bit samples (ICH4) */
1264235a317STakashi Iwai #define ICH_PCM_246_MASK 0x00300000 /* chan mask (not all chips) */
1274235a317STakashi Iwai #define ICH_PCM_8 0x00300000 /* 8 channels (not all chips) */
1281da177e4SLinus Torvalds #define ICH_PCM_6 0x00200000 /* 6 channels (not all chips) */
1291da177e4SLinus Torvalds #define ICH_PCM_4 0x00100000 /* 4 channels (not all chips) */
1301da177e4SLinus Torvalds #define ICH_PCM_2 0x00000000 /* 2 channels (stereo) */
1311da177e4SLinus Torvalds #define ICH_SIS_PCM_246_MASK 0x000000c0 /* 6 channels (SIS7012) */
1321da177e4SLinus Torvalds #define ICH_SIS_PCM_6 0x00000080 /* 6 channels (SIS7012) */
1331da177e4SLinus Torvalds #define ICH_SIS_PCM_4 0x00000040 /* 4 channels (SIS7012) */
1341da177e4SLinus Torvalds #define ICH_SIS_PCM_2 0x00000000 /* 2 channels (SIS7012) */
1351da177e4SLinus Torvalds #define ICH_TRIE 0x00000040 /* tertiary resume interrupt enable */
1361da177e4SLinus Torvalds #define ICH_SRIE 0x00000020 /* secondary resume interrupt enable */
1371da177e4SLinus Torvalds #define ICH_PRIE 0x00000010 /* primary resume interrupt enable */
1381da177e4SLinus Torvalds #define ICH_ACLINK 0x00000008 /* AClink shut off */
1391da177e4SLinus Torvalds #define ICH_AC97WARM 0x00000004 /* AC'97 warm reset */
1401da177e4SLinus Torvalds #define ICH_AC97COLD 0x00000002 /* AC'97 cold reset */
1411da177e4SLinus Torvalds #define ICH_GIE 0x00000001 /* GPI interrupt enable */
1421da177e4SLinus Torvalds #define ICH_REG_GLOB_STA 0x30 /* dword - global status */
1431da177e4SLinus Torvalds #define ICH_TRI 0x20000000 /* ICH4: tertiary (AC_SDIN2) resume interrupt */
1441da177e4SLinus Torvalds #define ICH_TCR 0x10000000 /* ICH4: tertiary (AC_SDIN2) codec ready */
1451da177e4SLinus Torvalds #define ICH_BCS 0x08000000 /* ICH4: bit clock stopped */
1461da177e4SLinus Torvalds #define ICH_SPINT 0x04000000 /* ICH4: S/PDIF interrupt */
1471da177e4SLinus Torvalds #define ICH_P2INT 0x02000000 /* ICH4: PCM2-In interrupt */
1481da177e4SLinus Torvalds #define ICH_M2INT 0x01000000 /* ICH4: Mic2-In interrupt */
1491da177e4SLinus Torvalds #define ICH_SAMPLE_CAP 0x00c00000 /* ICH4: sample capability bits (RO) */
1501da177e4SLinus Torvalds #define ICH_SAMPLE_16_20 0x00400000 /* ICH4: 16- and 20-bit samples */
1511da177e4SLinus Torvalds #define ICH_MULTICHAN_CAP 0x00300000 /* ICH4: multi-channel capability bits (RO) */
15284a43bd5STakashi Iwai #define ICH_SIS_TRI 0x00080000 /* SIS: tertiary resume irq */
15384a43bd5STakashi Iwai #define ICH_SIS_TCR 0x00040000 /* SIS: tertiary codec ready */
1541da177e4SLinus Torvalds #define ICH_MD3 0x00020000 /* modem power down semaphore */
1551da177e4SLinus Torvalds #define ICH_AD3 0x00010000 /* audio power down semaphore */
1561da177e4SLinus Torvalds #define ICH_RCS 0x00008000 /* read completion status */
1571da177e4SLinus Torvalds #define ICH_BIT3 0x00004000 /* bit 3 slot 12 */
1581da177e4SLinus Torvalds #define ICH_BIT2 0x00002000 /* bit 2 slot 12 */
1591da177e4SLinus Torvalds #define ICH_BIT1 0x00001000 /* bit 1 slot 12 */
1601da177e4SLinus Torvalds #define ICH_SRI 0x00000800 /* secondary (AC_SDIN1) resume interrupt */
1611da177e4SLinus Torvalds #define ICH_PRI 0x00000400 /* primary (AC_SDIN0) resume interrupt */
1621da177e4SLinus Torvalds #define ICH_SCR 0x00000200 /* secondary (AC_SDIN1) codec ready */
1631da177e4SLinus Torvalds #define ICH_PCR 0x00000100 /* primary (AC_SDIN0) codec ready */
1641da177e4SLinus Torvalds #define ICH_MCINT 0x00000080 /* MIC capture interrupt */
1651da177e4SLinus Torvalds #define ICH_POINT 0x00000040 /* playback interrupt */
1661da177e4SLinus Torvalds #define ICH_PIINT 0x00000020 /* capture interrupt */
1671da177e4SLinus Torvalds #define ICH_NVSPINT 0x00000010 /* nforce spdif interrupt */
1681da177e4SLinus Torvalds #define ICH_MOINT 0x00000004 /* modem playback interrupt */
1691da177e4SLinus Torvalds #define ICH_MIINT 0x00000002 /* modem capture interrupt */
1701da177e4SLinus Torvalds #define ICH_GSCI 0x00000001 /* GPI status change interrupt */
1711da177e4SLinus Torvalds #define ICH_REG_ACC_SEMA 0x34 /* byte - codec write semaphore */
1721da177e4SLinus Torvalds #define ICH_CAS 0x01 /* codec access semaphore */
1731da177e4SLinus Torvalds #define ICH_REG_SDM 0x80
1741da177e4SLinus Torvalds #define ICH_DI2L_MASK 0x000000c0 /* PCM In 2, Mic In 2 data in line */
1751da177e4SLinus Torvalds #define ICH_DI2L_SHIFT 6
1761da177e4SLinus Torvalds #define ICH_DI1L_MASK 0x00000030 /* PCM In 1, Mic In 1 data in line */
1771da177e4SLinus Torvalds #define ICH_DI1L_SHIFT 4
1781da177e4SLinus Torvalds #define ICH_SE 0x00000008 /* steer enable */
1791da177e4SLinus Torvalds #define ICH_LDI_MASK 0x00000003 /* last codec read data input */
1801da177e4SLinus Torvalds
1811da177e4SLinus Torvalds #define ICH_MAX_FRAGS 32 /* max hw frags */
1821da177e4SLinus Torvalds
1831da177e4SLinus Torvalds
1841da177e4SLinus Torvalds /*
1851da177e4SLinus Torvalds * registers for Ali5455
1861da177e4SLinus Torvalds */
1871da177e4SLinus Torvalds
1881da177e4SLinus Torvalds /* ALi 5455 busmaster blocks */
1891da177e4SLinus Torvalds DEFINE_REGSET(AL_PI, 0x40); /* ALi PCM in */
1901da177e4SLinus Torvalds DEFINE_REGSET(AL_PO, 0x50); /* Ali PCM out */
1911da177e4SLinus Torvalds DEFINE_REGSET(AL_MC, 0x60); /* Ali Mic in */
1921da177e4SLinus Torvalds DEFINE_REGSET(AL_CDC_SPO, 0x70); /* Ali Codec SPDIF out */
1931da177e4SLinus Torvalds DEFINE_REGSET(AL_CENTER, 0x80); /* Ali center out */
1941da177e4SLinus Torvalds DEFINE_REGSET(AL_LFE, 0x90); /* Ali center out */
1951da177e4SLinus Torvalds DEFINE_REGSET(AL_CLR_SPI, 0xa0); /* Ali Controller SPDIF in */
1961da177e4SLinus Torvalds DEFINE_REGSET(AL_CLR_SPO, 0xb0); /* Ali Controller SPDIF out */
1971da177e4SLinus Torvalds DEFINE_REGSET(AL_I2S, 0xc0); /* Ali I2S in */
1981da177e4SLinus Torvalds DEFINE_REGSET(AL_PI2, 0xd0); /* Ali PCM2 in */
1991da177e4SLinus Torvalds DEFINE_REGSET(AL_MC2, 0xe0); /* Ali Mic2 in */
2001da177e4SLinus Torvalds
2011da177e4SLinus Torvalds enum {
2021da177e4SLinus Torvalds ICH_REG_ALI_SCR = 0x00, /* System Control Register */
2031da177e4SLinus Torvalds ICH_REG_ALI_SSR = 0x04, /* System Status Register */
2041da177e4SLinus Torvalds ICH_REG_ALI_DMACR = 0x08, /* DMA Control Register */
2051da177e4SLinus Torvalds ICH_REG_ALI_FIFOCR1 = 0x0c, /* FIFO Control Register 1 */
2061da177e4SLinus Torvalds ICH_REG_ALI_INTERFACECR = 0x10, /* Interface Control Register */
2071da177e4SLinus Torvalds ICH_REG_ALI_INTERRUPTCR = 0x14, /* Interrupt control Register */
2081da177e4SLinus Torvalds ICH_REG_ALI_INTERRUPTSR = 0x18, /* Interrupt Status Register */
2091da177e4SLinus Torvalds ICH_REG_ALI_FIFOCR2 = 0x1c, /* FIFO Control Register 2 */
2101da177e4SLinus Torvalds ICH_REG_ALI_CPR = 0x20, /* Command Port Register */
2111da177e4SLinus Torvalds ICH_REG_ALI_CPR_ADDR = 0x22, /* ac97 addr write */
2121da177e4SLinus Torvalds ICH_REG_ALI_SPR = 0x24, /* Status Port Register */
2131da177e4SLinus Torvalds ICH_REG_ALI_SPR_ADDR = 0x26, /* ac97 addr read */
2141da177e4SLinus Torvalds ICH_REG_ALI_FIFOCR3 = 0x2c, /* FIFO Control Register 3 */
2151da177e4SLinus Torvalds ICH_REG_ALI_TTSR = 0x30, /* Transmit Tag Slot Register */
2161da177e4SLinus Torvalds ICH_REG_ALI_RTSR = 0x34, /* Receive Tag Slot Register */
2171da177e4SLinus Torvalds ICH_REG_ALI_CSPSR = 0x38, /* Command/Status Port Status Register */
2181da177e4SLinus Torvalds ICH_REG_ALI_CAS = 0x3c, /* Codec Write Semaphore Register */
2191da177e4SLinus Torvalds ICH_REG_ALI_HWVOL = 0xf0, /* hardware volume control/status */
2201da177e4SLinus Torvalds ICH_REG_ALI_I2SCR = 0xf4, /* I2S control/status */
2211da177e4SLinus Torvalds ICH_REG_ALI_SPDIFCSR = 0xf8, /* spdif channel status register */
2221da177e4SLinus Torvalds ICH_REG_ALI_SPDIFICS = 0xfc, /* spdif interface control/status */
2231da177e4SLinus Torvalds };
2241da177e4SLinus Torvalds
2251da177e4SLinus Torvalds #define ALI_CAS_SEM_BUSY 0x80000000
2261da177e4SLinus Torvalds #define ALI_CPR_ADDR_SECONDARY 0x100
2271da177e4SLinus Torvalds #define ALI_CPR_ADDR_READ 0x80
2281da177e4SLinus Torvalds #define ALI_CSPSR_CODEC_READY 0x08
2291da177e4SLinus Torvalds #define ALI_CSPSR_READ_OK 0x02
2301da177e4SLinus Torvalds #define ALI_CSPSR_WRITE_OK 0x01
2311da177e4SLinus Torvalds
2321da177e4SLinus Torvalds /* interrupts for the whole chip by interrupt status register finish */
2331da177e4SLinus Torvalds
2341da177e4SLinus Torvalds #define ALI_INT_MICIN2 (1<<26)
2351da177e4SLinus Torvalds #define ALI_INT_PCMIN2 (1<<25)
2361da177e4SLinus Torvalds #define ALI_INT_I2SIN (1<<24)
2371da177e4SLinus Torvalds #define ALI_INT_SPDIFOUT (1<<23) /* controller spdif out INTERRUPT */
2381da177e4SLinus Torvalds #define ALI_INT_SPDIFIN (1<<22)
2391da177e4SLinus Torvalds #define ALI_INT_LFEOUT (1<<21)
2401da177e4SLinus Torvalds #define ALI_INT_CENTEROUT (1<<20)
2411da177e4SLinus Torvalds #define ALI_INT_CODECSPDIFOUT (1<<19)
2421da177e4SLinus Torvalds #define ALI_INT_MICIN (1<<18)
2431da177e4SLinus Torvalds #define ALI_INT_PCMOUT (1<<17)
2441da177e4SLinus Torvalds #define ALI_INT_PCMIN (1<<16)
2451da177e4SLinus Torvalds #define ALI_INT_CPRAIS (1<<7) /* command port available */
2461da177e4SLinus Torvalds #define ALI_INT_SPRAIS (1<<5) /* status port available */
2471da177e4SLinus Torvalds #define ALI_INT_GPIO (1<<1)
2486b75a9d8STakashi Iwai #define ALI_INT_MASK (ALI_INT_SPDIFOUT|ALI_INT_CODECSPDIFOUT|\
2496b75a9d8STakashi Iwai ALI_INT_MICIN|ALI_INT_PCMOUT|ALI_INT_PCMIN)
2501da177e4SLinus Torvalds
2511da177e4SLinus Torvalds #define ICH_ALI_SC_RESET (1<<31) /* master reset */
2521da177e4SLinus Torvalds #define ICH_ALI_SC_AC97_DBL (1<<30)
2531da177e4SLinus Torvalds #define ICH_ALI_SC_CODEC_SPDF (3<<20) /* 1=7/8, 2=6/9, 3=10/11 */
2541da177e4SLinus Torvalds #define ICH_ALI_SC_IN_BITS (3<<18)
2551da177e4SLinus Torvalds #define ICH_ALI_SC_OUT_BITS (3<<16)
2561da177e4SLinus Torvalds #define ICH_ALI_SC_6CH_CFG (3<<14)
2571da177e4SLinus Torvalds #define ICH_ALI_SC_PCM_4 (1<<8)
2581da177e4SLinus Torvalds #define ICH_ALI_SC_PCM_6 (2<<8)
2591da177e4SLinus Torvalds #define ICH_ALI_SC_PCM_246_MASK (3<<8)
2601da177e4SLinus Torvalds
2611da177e4SLinus Torvalds #define ICH_ALI_SS_SEC_ID (3<<5)
2621da177e4SLinus Torvalds #define ICH_ALI_SS_PRI_ID (3<<3)
2631da177e4SLinus Torvalds
2641da177e4SLinus Torvalds #define ICH_ALI_IF_AC97SP (1<<21)
2651da177e4SLinus Torvalds #define ICH_ALI_IF_MC (1<<20)
2661da177e4SLinus Torvalds #define ICH_ALI_IF_PI (1<<19)
2671da177e4SLinus Torvalds #define ICH_ALI_IF_MC2 (1<<18)
2681da177e4SLinus Torvalds #define ICH_ALI_IF_PI2 (1<<17)
2691da177e4SLinus Torvalds #define ICH_ALI_IF_LINE_SRC (1<<15) /* 0/1 = slot 3/6 */
2701da177e4SLinus Torvalds #define ICH_ALI_IF_MIC_SRC (1<<14) /* 0/1 = slot 3/6 */
2711da177e4SLinus Torvalds #define ICH_ALI_IF_SPDF_SRC (3<<12) /* 00 = PCM, 01 = AC97-in, 10 = spdif-in, 11 = i2s */
2721da177e4SLinus Torvalds #define ICH_ALI_IF_AC97_OUT (3<<8) /* 00 = PCM, 10 = spdif-in, 11 = i2s */
2731da177e4SLinus Torvalds #define ICH_ALI_IF_PO_SPDF (1<<3)
2741da177e4SLinus Torvalds #define ICH_ALI_IF_PO (1<<1)
2751da177e4SLinus Torvalds
2761da177e4SLinus Torvalds /*
2771da177e4SLinus Torvalds *
2781da177e4SLinus Torvalds */
2791da177e4SLinus Torvalds
2806b75a9d8STakashi Iwai enum {
2816b75a9d8STakashi Iwai ICHD_PCMIN,
2826b75a9d8STakashi Iwai ICHD_PCMOUT,
2836b75a9d8STakashi Iwai ICHD_MIC,
2846b75a9d8STakashi Iwai ICHD_MIC2,
2856b75a9d8STakashi Iwai ICHD_PCM2IN,
2866b75a9d8STakashi Iwai ICHD_SPBAR,
2876b75a9d8STakashi Iwai ICHD_LAST = ICHD_SPBAR
2886b75a9d8STakashi Iwai };
2896b75a9d8STakashi Iwai enum {
2906b75a9d8STakashi Iwai NVD_PCMIN,
2916b75a9d8STakashi Iwai NVD_PCMOUT,
2926b75a9d8STakashi Iwai NVD_MIC,
2936b75a9d8STakashi Iwai NVD_SPBAR,
2946b75a9d8STakashi Iwai NVD_LAST = NVD_SPBAR
2956b75a9d8STakashi Iwai };
2966b75a9d8STakashi Iwai enum {
2976b75a9d8STakashi Iwai ALID_PCMIN,
2986b75a9d8STakashi Iwai ALID_PCMOUT,
2996b75a9d8STakashi Iwai ALID_MIC,
3006b75a9d8STakashi Iwai ALID_AC97SPDIFOUT,
3016b75a9d8STakashi Iwai ALID_SPDIFIN,
3026b75a9d8STakashi Iwai ALID_SPDIFOUT,
3036b75a9d8STakashi Iwai ALID_LAST = ALID_SPDIFOUT
3046b75a9d8STakashi Iwai };
3051da177e4SLinus Torvalds
3066b75a9d8STakashi Iwai #define get_ichdev(substream) (substream->runtime->private_data)
3071da177e4SLinus Torvalds
3086b75a9d8STakashi Iwai struct ichdev {
3091da177e4SLinus Torvalds unsigned int ichd; /* ich device number */
3101da177e4SLinus Torvalds unsigned long reg_offset; /* offset to bmaddr */
3113c164e2cSTakashi Iwai __le32 *bdbar; /* CPU address (32bit) */
3121da177e4SLinus Torvalds unsigned int bdbar_addr; /* PCI bus address (32bit) */
3136b75a9d8STakashi Iwai struct snd_pcm_substream *substream;
3141da177e4SLinus Torvalds unsigned int physbuf; /* physical address (32bit) */
3151da177e4SLinus Torvalds unsigned int size;
3161da177e4SLinus Torvalds unsigned int fragsize;
3171da177e4SLinus Torvalds unsigned int fragsize1;
3181da177e4SLinus Torvalds unsigned int position;
3191da177e4SLinus Torvalds unsigned int pos_shift;
320da2436a2SJaroslav Kysela unsigned int last_pos;
3211da177e4SLinus Torvalds int frags;
3221da177e4SLinus Torvalds int lvi;
3231da177e4SLinus Torvalds int lvi_frag;
3241da177e4SLinus Torvalds int civ;
3251da177e4SLinus Torvalds int ack;
3261da177e4SLinus Torvalds int ack_reload;
3271da177e4SLinus Torvalds unsigned int ack_bit;
3281da177e4SLinus Torvalds unsigned int roff_sr;
3291da177e4SLinus Torvalds unsigned int roff_picb;
3301da177e4SLinus Torvalds unsigned int int_sta_mask; /* interrupt status mask */
3311da177e4SLinus Torvalds unsigned int ali_slot; /* ALI DMA slot */
3321da177e4SLinus Torvalds struct ac97_pcm *pcm;
3331da177e4SLinus Torvalds int pcm_open_flag;
334c1f06161STakashi Iwai unsigned int prepared:1;
3351cfe43d2STakashi Iwai unsigned int suspended: 1;
3366b75a9d8STakashi Iwai };
3371da177e4SLinus Torvalds
3386b75a9d8STakashi Iwai struct intel8x0 {
3391da177e4SLinus Torvalds unsigned int device_type;
3401da177e4SLinus Torvalds
3411da177e4SLinus Torvalds int irq;
3421da177e4SLinus Torvalds
3433388c37eSTakashi Iwai void __iomem *addr;
3443388c37eSTakashi Iwai void __iomem *bmaddr;
3451da177e4SLinus Torvalds
3461da177e4SLinus Torvalds struct pci_dev *pci;
3476b75a9d8STakashi Iwai struct snd_card *card;
3481da177e4SLinus Torvalds
3491da177e4SLinus Torvalds int pcm_devs;
3506b75a9d8STakashi Iwai struct snd_pcm *pcm[6];
3516b75a9d8STakashi Iwai struct ichdev ichd[6];
3521da177e4SLinus Torvalds
3531da177e4SLinus Torvalds unsigned multi4: 1,
3541da177e4SLinus Torvalds multi6: 1,
3554235a317STakashi Iwai multi8 :1,
3561da177e4SLinus Torvalds dra: 1,
3571da177e4SLinus Torvalds smp20bit: 1;
3581da177e4SLinus Torvalds unsigned in_ac97_init: 1,
3591da177e4SLinus Torvalds in_sdin_init: 1;
3601da177e4SLinus Torvalds unsigned in_measurement: 1; /* during ac97 clock measurement */
3611da177e4SLinus Torvalds unsigned fix_nocache: 1; /* workaround for 440MX */
3621da177e4SLinus Torvalds unsigned buggy_irq: 1; /* workaround for buggy mobos */
3631da177e4SLinus Torvalds unsigned xbox: 1; /* workaround for Xbox AC'97 detection */
364a06147d2STakashi Iwai unsigned buggy_semaphore: 1; /* workaround for buggy codec semaphore */
365228cf793SKonstantin Ozerkov unsigned inside_vm: 1; /* enable VM optimization */
3661da177e4SLinus Torvalds
3671da177e4SLinus Torvalds int spdif_idx; /* SPDIF BAR index; *_SPBAR or -1 if use PCMOUT */
36852b72388STakashi Iwai unsigned int sdm_saved; /* SDM reg value */
3691da177e4SLinus Torvalds
3706b75a9d8STakashi Iwai struct snd_ac97_bus *ac97_bus;
3716b75a9d8STakashi Iwai struct snd_ac97 *ac97[3];
3721da177e4SLinus Torvalds unsigned int ac97_sdin[3];
37384a43bd5STakashi Iwai unsigned int max_codecs, ncodecs;
374f729f88aSTakashi Iwai const unsigned int *codec_bit;
37584a43bd5STakashi Iwai unsigned int codec_isr_bits;
37684a43bd5STakashi Iwai unsigned int codec_ready_bits;
3771da177e4SLinus Torvalds
3781da177e4SLinus Torvalds spinlock_t reg_lock;
3791da177e4SLinus Torvalds
3801da177e4SLinus Torvalds u32 bdbars_count;
3817835e090STakashi Iwai struct snd_dma_buffer *bdbars;
3821da177e4SLinus Torvalds u32 int_sta_reg; /* interrupt status register */
3831da177e4SLinus Torvalds u32 int_sta_mask; /* interrupt status mask */
3841da177e4SLinus Torvalds };
3851da177e4SLinus Torvalds
3869baa3c34SBenoit Taine static const struct pci_device_id snd_intel8x0_ids[] = {
38728d27aaeSJoe Perches { PCI_VDEVICE(INTEL, 0x2415), DEVICE_INTEL }, /* 82801AA */
38828d27aaeSJoe Perches { PCI_VDEVICE(INTEL, 0x2425), DEVICE_INTEL }, /* 82901AB */
38928d27aaeSJoe Perches { PCI_VDEVICE(INTEL, 0x2445), DEVICE_INTEL }, /* 82801BA */
39028d27aaeSJoe Perches { PCI_VDEVICE(INTEL, 0x2485), DEVICE_INTEL }, /* ICH3 */
39128d27aaeSJoe Perches { PCI_VDEVICE(INTEL, 0x24c5), DEVICE_INTEL_ICH4 }, /* ICH4 */
39228d27aaeSJoe Perches { PCI_VDEVICE(INTEL, 0x24d5), DEVICE_INTEL_ICH4 }, /* ICH5 */
39328d27aaeSJoe Perches { PCI_VDEVICE(INTEL, 0x25a6), DEVICE_INTEL_ICH4 }, /* ESB */
39428d27aaeSJoe Perches { PCI_VDEVICE(INTEL, 0x266e), DEVICE_INTEL_ICH4 }, /* ICH6 */
39528d27aaeSJoe Perches { PCI_VDEVICE(INTEL, 0x27de), DEVICE_INTEL_ICH4 }, /* ICH7 */
39628d27aaeSJoe Perches { PCI_VDEVICE(INTEL, 0x2698), DEVICE_INTEL_ICH4 }, /* ESB2 */
39728d27aaeSJoe Perches { PCI_VDEVICE(INTEL, 0x7195), DEVICE_INTEL }, /* 440MX */
39828d27aaeSJoe Perches { PCI_VDEVICE(SI, 0x7012), DEVICE_SIS }, /* SI7012 */
39928d27aaeSJoe Perches { PCI_VDEVICE(NVIDIA, 0x01b1), DEVICE_NFORCE }, /* NFORCE */
40028d27aaeSJoe Perches { PCI_VDEVICE(NVIDIA, 0x003a), DEVICE_NFORCE }, /* MCP04 */
40128d27aaeSJoe Perches { PCI_VDEVICE(NVIDIA, 0x006a), DEVICE_NFORCE }, /* NFORCE2 */
40228d27aaeSJoe Perches { PCI_VDEVICE(NVIDIA, 0x0059), DEVICE_NFORCE }, /* CK804 */
40328d27aaeSJoe Perches { PCI_VDEVICE(NVIDIA, 0x008a), DEVICE_NFORCE }, /* CK8 */
40428d27aaeSJoe Perches { PCI_VDEVICE(NVIDIA, 0x00da), DEVICE_NFORCE }, /* NFORCE3 */
40528d27aaeSJoe Perches { PCI_VDEVICE(NVIDIA, 0x00ea), DEVICE_NFORCE }, /* CK8S */
40628d27aaeSJoe Perches { PCI_VDEVICE(NVIDIA, 0x026b), DEVICE_NFORCE }, /* MCP51 */
40728d27aaeSJoe Perches { PCI_VDEVICE(AMD, 0x746d), DEVICE_INTEL }, /* AMD8111 */
40828d27aaeSJoe Perches { PCI_VDEVICE(AMD, 0x7445), DEVICE_INTEL }, /* AMD768 */
40928d27aaeSJoe Perches { PCI_VDEVICE(AL, 0x5455), DEVICE_ALI }, /* Ali5455 */
4101da177e4SLinus Torvalds { 0, }
4111da177e4SLinus Torvalds };
4121da177e4SLinus Torvalds
4131da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, snd_intel8x0_ids);
4141da177e4SLinus Torvalds
4151da177e4SLinus Torvalds /*
4161da177e4SLinus Torvalds * Lowlevel I/O - busmaster
4171da177e4SLinus Torvalds */
4181da177e4SLinus Torvalds
igetbyte(struct intel8x0 * chip,u32 offset)4193388c37eSTakashi Iwai static inline u8 igetbyte(struct intel8x0 *chip, u32 offset)
4201da177e4SLinus Torvalds {
4213388c37eSTakashi Iwai return ioread8(chip->bmaddr + offset);
4221da177e4SLinus Torvalds }
4231da177e4SLinus Torvalds
igetword(struct intel8x0 * chip,u32 offset)4243388c37eSTakashi Iwai static inline u16 igetword(struct intel8x0 *chip, u32 offset)
4251da177e4SLinus Torvalds {
4263388c37eSTakashi Iwai return ioread16(chip->bmaddr + offset);
4271da177e4SLinus Torvalds }
4281da177e4SLinus Torvalds
igetdword(struct intel8x0 * chip,u32 offset)4293388c37eSTakashi Iwai static inline u32 igetdword(struct intel8x0 *chip, u32 offset)
4301da177e4SLinus Torvalds {
4313388c37eSTakashi Iwai return ioread32(chip->bmaddr + offset);
4321da177e4SLinus Torvalds }
4331da177e4SLinus Torvalds
iputbyte(struct intel8x0 * chip,u32 offset,u8 val)4343388c37eSTakashi Iwai static inline void iputbyte(struct intel8x0 *chip, u32 offset, u8 val)
4351da177e4SLinus Torvalds {
4363388c37eSTakashi Iwai iowrite8(val, chip->bmaddr + offset);
4371da177e4SLinus Torvalds }
4381da177e4SLinus Torvalds
iputword(struct intel8x0 * chip,u32 offset,u16 val)4393388c37eSTakashi Iwai static inline void iputword(struct intel8x0 *chip, u32 offset, u16 val)
4401da177e4SLinus Torvalds {
4413388c37eSTakashi Iwai iowrite16(val, chip->bmaddr + offset);
4421da177e4SLinus Torvalds }
4431da177e4SLinus Torvalds
iputdword(struct intel8x0 * chip,u32 offset,u32 val)4443388c37eSTakashi Iwai static inline void iputdword(struct intel8x0 *chip, u32 offset, u32 val)
4451da177e4SLinus Torvalds {
4463388c37eSTakashi Iwai iowrite32(val, chip->bmaddr + offset);
4471da177e4SLinus Torvalds }
4481da177e4SLinus Torvalds
4491da177e4SLinus Torvalds /*
4501da177e4SLinus Torvalds * Lowlevel I/O - AC'97 registers
4511da177e4SLinus Torvalds */
4521da177e4SLinus Torvalds
iagetword(struct intel8x0 * chip,u32 offset)4533388c37eSTakashi Iwai static inline u16 iagetword(struct intel8x0 *chip, u32 offset)
4541da177e4SLinus Torvalds {
4553388c37eSTakashi Iwai return ioread16(chip->addr + offset);
4561da177e4SLinus Torvalds }
4571da177e4SLinus Torvalds
iaputword(struct intel8x0 * chip,u32 offset,u16 val)4583388c37eSTakashi Iwai static inline void iaputword(struct intel8x0 *chip, u32 offset, u16 val)
4591da177e4SLinus Torvalds {
4603388c37eSTakashi Iwai iowrite16(val, chip->addr + offset);
4611da177e4SLinus Torvalds }
4621da177e4SLinus Torvalds
4631da177e4SLinus Torvalds /*
4641da177e4SLinus Torvalds * Basic I/O
4651da177e4SLinus Torvalds */
4661da177e4SLinus Torvalds
4671da177e4SLinus Torvalds /*
4681da177e4SLinus Torvalds * access to AC97 codec via normal i/o (for ICH and SIS7012)
4691da177e4SLinus Torvalds */
4701da177e4SLinus Torvalds
snd_intel8x0_codec_semaphore(struct intel8x0 * chip,unsigned int codec)4716b75a9d8STakashi Iwai static int snd_intel8x0_codec_semaphore(struct intel8x0 *chip, unsigned int codec)
4721da177e4SLinus Torvalds {
4731da177e4SLinus Torvalds int time;
4741da177e4SLinus Torvalds
4751da177e4SLinus Torvalds if (codec > 2)
4761da177e4SLinus Torvalds return -EIO;
4771da177e4SLinus Torvalds if (chip->in_sdin_init) {
4781da177e4SLinus Torvalds /* we don't know the ready bit assignment at the moment */
4791da177e4SLinus Torvalds /* so we check any */
48084a43bd5STakashi Iwai codec = chip->codec_isr_bits;
4811da177e4SLinus Torvalds } else {
48284a43bd5STakashi Iwai codec = chip->codec_bit[chip->ac97_sdin[codec]];
4831da177e4SLinus Torvalds }
4841da177e4SLinus Torvalds
4851da177e4SLinus Torvalds /* codec ready ? */
4861da177e4SLinus Torvalds if ((igetdword(chip, ICHREG(GLOB_STA)) & codec) == 0)
4871da177e4SLinus Torvalds return -EIO;
4881da177e4SLinus Torvalds
489a06147d2STakashi Iwai if (chip->buggy_semaphore)
490a06147d2STakashi Iwai return 0; /* just ignore ... */
491a06147d2STakashi Iwai
4921da177e4SLinus Torvalds /* Anyone holding a semaphore for 1 msec should be shot... */
4931da177e4SLinus Torvalds time = 100;
4941da177e4SLinus Torvalds do {
4951da177e4SLinus Torvalds if (!(igetbyte(chip, ICHREG(ACC_SEMA)) & ICH_CAS))
4961da177e4SLinus Torvalds return 0;
4971da177e4SLinus Torvalds udelay(10);
4981da177e4SLinus Torvalds } while (time--);
4991da177e4SLinus Torvalds
50025985edcSLucas De Marchi /* access to some forbidden (non existent) ac97 registers will not
5011da177e4SLinus Torvalds * reset the semaphore. So even if you don't get the semaphore, still
5021da177e4SLinus Torvalds * continue the access. We don't need the semaphore anyway. */
503f493e7bcSTakashi Iwai dev_err(chip->card->dev,
504f493e7bcSTakashi Iwai "codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
5051da177e4SLinus Torvalds igetbyte(chip, ICHREG(ACC_SEMA)), igetdword(chip, ICHREG(GLOB_STA)));
5061da177e4SLinus Torvalds iagetword(chip, 0); /* clear semaphore flag */
5071da177e4SLinus Torvalds /* I don't care about the semaphore */
5081da177e4SLinus Torvalds return -EBUSY;
5091da177e4SLinus Torvalds }
5101da177e4SLinus Torvalds
snd_intel8x0_codec_write(struct snd_ac97 * ac97,unsigned short reg,unsigned short val)5116b75a9d8STakashi Iwai static void snd_intel8x0_codec_write(struct snd_ac97 *ac97,
5121da177e4SLinus Torvalds unsigned short reg,
5131da177e4SLinus Torvalds unsigned short val)
5141da177e4SLinus Torvalds {
5156b75a9d8STakashi Iwai struct intel8x0 *chip = ac97->private_data;
5161da177e4SLinus Torvalds
5171da177e4SLinus Torvalds if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) {
5181da177e4SLinus Torvalds if (! chip->in_ac97_init)
519f493e7bcSTakashi Iwai dev_err(chip->card->dev,
520f493e7bcSTakashi Iwai "codec_write %d: semaphore is not ready for register 0x%x\n",
521f493e7bcSTakashi Iwai ac97->num, reg);
5221da177e4SLinus Torvalds }
5231da177e4SLinus Torvalds iaputword(chip, reg + ac97->num * 0x80, val);
5241da177e4SLinus Torvalds }
5251da177e4SLinus Torvalds
snd_intel8x0_codec_read(struct snd_ac97 * ac97,unsigned short reg)5266b75a9d8STakashi Iwai static unsigned short snd_intel8x0_codec_read(struct snd_ac97 *ac97,
5271da177e4SLinus Torvalds unsigned short reg)
5281da177e4SLinus Torvalds {
5296b75a9d8STakashi Iwai struct intel8x0 *chip = ac97->private_data;
5301da177e4SLinus Torvalds unsigned short res;
5311da177e4SLinus Torvalds unsigned int tmp;
5321da177e4SLinus Torvalds
5331da177e4SLinus Torvalds if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) {
5341da177e4SLinus Torvalds if (! chip->in_ac97_init)
535f493e7bcSTakashi Iwai dev_err(chip->card->dev,
536f493e7bcSTakashi Iwai "codec_read %d: semaphore is not ready for register 0x%x\n",
537f493e7bcSTakashi Iwai ac97->num, reg);
5381da177e4SLinus Torvalds res = 0xffff;
5391da177e4SLinus Torvalds } else {
5401da177e4SLinus Torvalds res = iagetword(chip, reg + ac97->num * 0x80);
5413a5f3dd3STakashi Iwai tmp = igetdword(chip, ICHREG(GLOB_STA));
5423a5f3dd3STakashi Iwai if (tmp & ICH_RCS) {
5431da177e4SLinus Torvalds /* reset RCS and preserve other R/WC bits */
5446b75a9d8STakashi Iwai iputdword(chip, ICHREG(GLOB_STA), tmp &
54584a43bd5STakashi Iwai ~(chip->codec_ready_bits | ICH_GSCI));
5461da177e4SLinus Torvalds if (! chip->in_ac97_init)
547f493e7bcSTakashi Iwai dev_err(chip->card->dev,
548f493e7bcSTakashi Iwai "codec_read %d: read timeout for register 0x%x\n",
549f493e7bcSTakashi Iwai ac97->num, reg);
5501da177e4SLinus Torvalds res = 0xffff;
5511da177e4SLinus Torvalds }
5521da177e4SLinus Torvalds }
5531da177e4SLinus Torvalds return res;
5541da177e4SLinus Torvalds }
5551da177e4SLinus Torvalds
snd_intel8x0_codec_read_test(struct intel8x0 * chip,unsigned int codec)556e23e7a14SBill Pemberton static void snd_intel8x0_codec_read_test(struct intel8x0 *chip,
55784a43bd5STakashi Iwai unsigned int codec)
5581da177e4SLinus Torvalds {
5591da177e4SLinus Torvalds unsigned int tmp;
5601da177e4SLinus Torvalds
5611da177e4SLinus Torvalds if (snd_intel8x0_codec_semaphore(chip, codec) >= 0) {
5621da177e4SLinus Torvalds iagetword(chip, codec * 0x80);
5633a5f3dd3STakashi Iwai tmp = igetdword(chip, ICHREG(GLOB_STA));
5643a5f3dd3STakashi Iwai if (tmp & ICH_RCS) {
5651da177e4SLinus Torvalds /* reset RCS and preserve other R/WC bits */
5666b75a9d8STakashi Iwai iputdword(chip, ICHREG(GLOB_STA), tmp &
56784a43bd5STakashi Iwai ~(chip->codec_ready_bits | ICH_GSCI));
5681da177e4SLinus Torvalds }
5691da177e4SLinus Torvalds }
5701da177e4SLinus Torvalds }
5711da177e4SLinus Torvalds
5721da177e4SLinus Torvalds /*
5731da177e4SLinus Torvalds * access to AC97 for Ali5455
5741da177e4SLinus Torvalds */
snd_intel8x0_ali_codec_ready(struct intel8x0 * chip,int mask)5756b75a9d8STakashi Iwai static int snd_intel8x0_ali_codec_ready(struct intel8x0 *chip, int mask)
5761da177e4SLinus Torvalds {
5771da177e4SLinus Torvalds int count = 0;
5781da177e4SLinus Torvalds for (count = 0; count < 0x7f; count++) {
5791da177e4SLinus Torvalds int val = igetbyte(chip, ICHREG(ALI_CSPSR));
5801da177e4SLinus Torvalds if (val & mask)
5811da177e4SLinus Torvalds return 0;
5821da177e4SLinus Torvalds }
58379ba34b9STakashi Iwai if (! chip->in_ac97_init)
584f493e7bcSTakashi Iwai dev_warn(chip->card->dev, "AC97 codec ready timeout.\n");
5851da177e4SLinus Torvalds return -EBUSY;
5861da177e4SLinus Torvalds }
5871da177e4SLinus Torvalds
snd_intel8x0_ali_codec_semaphore(struct intel8x0 * chip)5886b75a9d8STakashi Iwai static int snd_intel8x0_ali_codec_semaphore(struct intel8x0 *chip)
5891da177e4SLinus Torvalds {
5901da177e4SLinus Torvalds int time = 100;
59179ba34b9STakashi Iwai if (chip->buggy_semaphore)
59279ba34b9STakashi Iwai return 0; /* just ignore ... */
59367d8a3c1SRoel Kluin while (--time && (igetdword(chip, ICHREG(ALI_CAS)) & ALI_CAS_SEM_BUSY))
5941da177e4SLinus Torvalds udelay(1);
59579ba34b9STakashi Iwai if (! time && ! chip->in_ac97_init)
596f493e7bcSTakashi Iwai dev_warn(chip->card->dev, "ali_codec_semaphore timeout\n");
5971da177e4SLinus Torvalds return snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_CODEC_READY);
5981da177e4SLinus Torvalds }
5991da177e4SLinus Torvalds
snd_intel8x0_ali_codec_read(struct snd_ac97 * ac97,unsigned short reg)6006b75a9d8STakashi Iwai static unsigned short snd_intel8x0_ali_codec_read(struct snd_ac97 *ac97, unsigned short reg)
6011da177e4SLinus Torvalds {
6026b75a9d8STakashi Iwai struct intel8x0 *chip = ac97->private_data;
6031da177e4SLinus Torvalds unsigned short data = 0xffff;
6041da177e4SLinus Torvalds
6051da177e4SLinus Torvalds if (snd_intel8x0_ali_codec_semaphore(chip))
6061da177e4SLinus Torvalds goto __err;
6071da177e4SLinus Torvalds reg |= ALI_CPR_ADDR_READ;
6081da177e4SLinus Torvalds if (ac97->num)
6091da177e4SLinus Torvalds reg |= ALI_CPR_ADDR_SECONDARY;
6101da177e4SLinus Torvalds iputword(chip, ICHREG(ALI_CPR_ADDR), reg);
6111da177e4SLinus Torvalds if (snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_READ_OK))
6121da177e4SLinus Torvalds goto __err;
6131da177e4SLinus Torvalds data = igetword(chip, ICHREG(ALI_SPR));
6141da177e4SLinus Torvalds __err:
6151da177e4SLinus Torvalds return data;
6161da177e4SLinus Torvalds }
6171da177e4SLinus Torvalds
snd_intel8x0_ali_codec_write(struct snd_ac97 * ac97,unsigned short reg,unsigned short val)6186b75a9d8STakashi Iwai static void snd_intel8x0_ali_codec_write(struct snd_ac97 *ac97, unsigned short reg,
6196b75a9d8STakashi Iwai unsigned short val)
6201da177e4SLinus Torvalds {
6216b75a9d8STakashi Iwai struct intel8x0 *chip = ac97->private_data;
6221da177e4SLinus Torvalds
6231da177e4SLinus Torvalds if (snd_intel8x0_ali_codec_semaphore(chip))
6241da177e4SLinus Torvalds return;
6251da177e4SLinus Torvalds iputword(chip, ICHREG(ALI_CPR), val);
6261da177e4SLinus Torvalds if (ac97->num)
6271da177e4SLinus Torvalds reg |= ALI_CPR_ADDR_SECONDARY;
6281da177e4SLinus Torvalds iputword(chip, ICHREG(ALI_CPR_ADDR), reg);
6291da177e4SLinus Torvalds snd_intel8x0_ali_codec_ready(chip, ALI_CSPSR_WRITE_OK);
6301da177e4SLinus Torvalds }
6311da177e4SLinus Torvalds
6321da177e4SLinus Torvalds
6331da177e4SLinus Torvalds /*
6341da177e4SLinus Torvalds * DMA I/O
6351da177e4SLinus Torvalds */
snd_intel8x0_setup_periods(struct intel8x0 * chip,struct ichdev * ichdev)6366b75a9d8STakashi Iwai static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ichdev)
6371da177e4SLinus Torvalds {
6381da177e4SLinus Torvalds int idx;
6393c164e2cSTakashi Iwai __le32 *bdbar = ichdev->bdbar;
6401da177e4SLinus Torvalds unsigned long port = ichdev->reg_offset;
6411da177e4SLinus Torvalds
6421da177e4SLinus Torvalds iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
6431da177e4SLinus Torvalds if (ichdev->size == ichdev->fragsize) {
6441da177e4SLinus Torvalds ichdev->ack_reload = ichdev->ack = 2;
6451da177e4SLinus Torvalds ichdev->fragsize1 = ichdev->fragsize >> 1;
6461da177e4SLinus Torvalds for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 4) {
6471da177e4SLinus Torvalds bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf);
6481da177e4SLinus Torvalds bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
6491da177e4SLinus Torvalds ichdev->fragsize1 >> ichdev->pos_shift);
6501da177e4SLinus Torvalds bdbar[idx + 2] = cpu_to_le32(ichdev->physbuf + (ichdev->size >> 1));
6511da177e4SLinus Torvalds bdbar[idx + 3] = cpu_to_le32(0x80000000 | /* interrupt on completion */
6521da177e4SLinus Torvalds ichdev->fragsize1 >> ichdev->pos_shift);
6531da177e4SLinus Torvalds }
6541da177e4SLinus Torvalds ichdev->frags = 2;
6551da177e4SLinus Torvalds } else {
6561da177e4SLinus Torvalds ichdev->ack_reload = ichdev->ack = 1;
6571da177e4SLinus Torvalds ichdev->fragsize1 = ichdev->fragsize;
6581da177e4SLinus Torvalds for (idx = 0; idx < (ICH_REG_LVI_MASK + 1) * 2; idx += 2) {
6596b75a9d8STakashi Iwai bdbar[idx + 0] = cpu_to_le32(ichdev->physbuf +
6606b75a9d8STakashi Iwai (((idx >> 1) * ichdev->fragsize) %
6616b75a9d8STakashi Iwai ichdev->size));
6621da177e4SLinus Torvalds bdbar[idx + 1] = cpu_to_le32(0x80000000 | /* interrupt on completion */
6631da177e4SLinus Torvalds ichdev->fragsize >> ichdev->pos_shift);
6646b75a9d8STakashi Iwai #if 0
665f493e7bcSTakashi Iwai dev_dbg(chip->card->dev, "bdbar[%i] = 0x%x [0x%x]\n",
6666b75a9d8STakashi Iwai idx + 0, bdbar[idx + 0], bdbar[idx + 1]);
6676b75a9d8STakashi Iwai #endif
6681da177e4SLinus Torvalds }
6691da177e4SLinus Torvalds ichdev->frags = ichdev->size / ichdev->fragsize;
6701da177e4SLinus Torvalds }
6711da177e4SLinus Torvalds iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi = ICH_REG_LVI_MASK);
6721da177e4SLinus Torvalds ichdev->civ = 0;
6731da177e4SLinus Torvalds iputbyte(chip, port + ICH_REG_OFF_CIV, 0);
6741da177e4SLinus Torvalds ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
6751da177e4SLinus Torvalds ichdev->position = 0;
6761da177e4SLinus Torvalds #if 0
677f493e7bcSTakashi Iwai dev_dbg(chip->card->dev,
678f493e7bcSTakashi Iwai "lvi_frag = %i, frags = %i, period_size = 0x%x, period_size1 = 0x%x\n",
67914ab0861STakashi Iwai ichdev->lvi_frag, ichdev->frags, ichdev->fragsize,
68014ab0861STakashi Iwai ichdev->fragsize1);
6811da177e4SLinus Torvalds #endif
6821da177e4SLinus Torvalds /* clear interrupts */
6831da177e4SLinus Torvalds iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
6841da177e4SLinus Torvalds }
6851da177e4SLinus Torvalds
6861da177e4SLinus Torvalds /*
6871da177e4SLinus Torvalds * Interrupt handler
6881da177e4SLinus Torvalds */
6891da177e4SLinus Torvalds
snd_intel8x0_update(struct intel8x0 * chip,struct ichdev * ichdev)6906b75a9d8STakashi Iwai static inline void snd_intel8x0_update(struct intel8x0 *chip, struct ichdev *ichdev)
6911da177e4SLinus Torvalds {
6921da177e4SLinus Torvalds unsigned long port = ichdev->reg_offset;
693883be793STakashi Iwai unsigned long flags;
6941da177e4SLinus Torvalds int status, civ, i, step;
6951da177e4SLinus Torvalds int ack = 0;
6961da177e4SLinus Torvalds
69724d1e494STakashi Iwai if (!(ichdev->prepared || chip->in_measurement) || ichdev->suspended)
698c1f06161STakashi Iwai return;
699c1f06161STakashi Iwai
700883be793STakashi Iwai spin_lock_irqsave(&chip->reg_lock, flags);
7011da177e4SLinus Torvalds status = igetbyte(chip, port + ichdev->roff_sr);
7021da177e4SLinus Torvalds civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
7031da177e4SLinus Torvalds if (!(status & ICH_BCIS)) {
7041da177e4SLinus Torvalds step = 0;
7051da177e4SLinus Torvalds } else if (civ == ichdev->civ) {
7061da177e4SLinus Torvalds // snd_printd("civ same %d\n", civ);
7071da177e4SLinus Torvalds step = 1;
7081da177e4SLinus Torvalds ichdev->civ++;
7091da177e4SLinus Torvalds ichdev->civ &= ICH_REG_LVI_MASK;
7101da177e4SLinus Torvalds } else {
7111da177e4SLinus Torvalds step = civ - ichdev->civ;
7121da177e4SLinus Torvalds if (step < 0)
7131da177e4SLinus Torvalds step += ICH_REG_LVI_MASK + 1;
7141da177e4SLinus Torvalds // if (step != 1)
7151da177e4SLinus Torvalds // snd_printd("step = %d, %d -> %d\n", step, ichdev->civ, civ);
7161da177e4SLinus Torvalds ichdev->civ = civ;
7171da177e4SLinus Torvalds }
7181da177e4SLinus Torvalds
7191da177e4SLinus Torvalds ichdev->position += step * ichdev->fragsize1;
7201da177e4SLinus Torvalds if (! chip->in_measurement)
7211da177e4SLinus Torvalds ichdev->position %= ichdev->size;
7221da177e4SLinus Torvalds ichdev->lvi += step;
7231da177e4SLinus Torvalds ichdev->lvi &= ICH_REG_LVI_MASK;
7241da177e4SLinus Torvalds iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi);
7251da177e4SLinus Torvalds for (i = 0; i < step; i++) {
7261da177e4SLinus Torvalds ichdev->lvi_frag++;
7271da177e4SLinus Torvalds ichdev->lvi_frag %= ichdev->frags;
7281da177e4SLinus Torvalds ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1);
7296b75a9d8STakashi Iwai #if 0
730f493e7bcSTakashi Iwai dev_dbg(chip->card->dev,
731f493e7bcSTakashi Iwai "new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n",
7326b75a9d8STakashi Iwai ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2],
7336b75a9d8STakashi Iwai ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port),
7346b75a9d8STakashi Iwai inl(port + 4), inb(port + ICH_REG_OFF_CR));
7356b75a9d8STakashi Iwai #endif
7361da177e4SLinus Torvalds if (--ichdev->ack == 0) {
7371da177e4SLinus Torvalds ichdev->ack = ichdev->ack_reload;
7381da177e4SLinus Torvalds ack = 1;
7391da177e4SLinus Torvalds }
7401da177e4SLinus Torvalds }
741883be793STakashi Iwai spin_unlock_irqrestore(&chip->reg_lock, flags);
7421da177e4SLinus Torvalds if (ack && ichdev->substream) {
7431da177e4SLinus Torvalds snd_pcm_period_elapsed(ichdev->substream);
7441da177e4SLinus Torvalds }
7451da177e4SLinus Torvalds iputbyte(chip, port + ichdev->roff_sr,
7461da177e4SLinus Torvalds status & (ICH_FIFOE | ICH_BCIS | ICH_LVBCI));
7471da177e4SLinus Torvalds }
7481da177e4SLinus Torvalds
snd_intel8x0_interrupt(int irq,void * dev_id)7497d12e780SDavid Howells static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id)
7501da177e4SLinus Torvalds {
7516b75a9d8STakashi Iwai struct intel8x0 *chip = dev_id;
7526b75a9d8STakashi Iwai struct ichdev *ichdev;
7531da177e4SLinus Torvalds unsigned int status;
7541da177e4SLinus Torvalds unsigned int i;
7551da177e4SLinus Torvalds
7561da177e4SLinus Torvalds status = igetdword(chip, chip->int_sta_reg);
7571da177e4SLinus Torvalds if (status == 0xffffffff) /* we are not yet resumed */
7581da177e4SLinus Torvalds return IRQ_NONE;
7591da177e4SLinus Torvalds
7601da177e4SLinus Torvalds if ((status & chip->int_sta_mask) == 0) {
7611da177e4SLinus Torvalds if (status) {
7621da177e4SLinus Torvalds /* ack */
7631da177e4SLinus Torvalds iputdword(chip, chip->int_sta_reg, status);
7641da177e4SLinus Torvalds if (! chip->buggy_irq)
7651da177e4SLinus Torvalds status = 0;
7661da177e4SLinus Torvalds }
7671da177e4SLinus Torvalds return IRQ_RETVAL(status);
7681da177e4SLinus Torvalds }
7691da177e4SLinus Torvalds
7701da177e4SLinus Torvalds for (i = 0; i < chip->bdbars_count; i++) {
7711da177e4SLinus Torvalds ichdev = &chip->ichd[i];
7721da177e4SLinus Torvalds if (status & ichdev->int_sta_mask)
7731da177e4SLinus Torvalds snd_intel8x0_update(chip, ichdev);
7741da177e4SLinus Torvalds }
7751da177e4SLinus Torvalds
7761da177e4SLinus Torvalds /* ack them */
7771da177e4SLinus Torvalds iputdword(chip, chip->int_sta_reg, status & chip->int_sta_mask);
7781da177e4SLinus Torvalds
7791da177e4SLinus Torvalds return IRQ_HANDLED;
7801da177e4SLinus Torvalds }
7811da177e4SLinus Torvalds
7821da177e4SLinus Torvalds /*
7831da177e4SLinus Torvalds * PCM part
7841da177e4SLinus Torvalds */
7851da177e4SLinus Torvalds
snd_intel8x0_pcm_trigger(struct snd_pcm_substream * substream,int cmd)7866b75a9d8STakashi Iwai static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
7871da177e4SLinus Torvalds {
7886b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
7896b75a9d8STakashi Iwai struct ichdev *ichdev = get_ichdev(substream);
7901da177e4SLinus Torvalds unsigned char val = 0;
7911da177e4SLinus Torvalds unsigned long port = ichdev->reg_offset;
7921da177e4SLinus Torvalds
7931da177e4SLinus Torvalds switch (cmd) {
7941da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_RESUME:
7951cfe43d2STakashi Iwai ichdev->suspended = 0;
796c0dbbdadSGustavo A. R. Silva fallthrough;
7971cfe43d2STakashi Iwai case SNDRV_PCM_TRIGGER_START:
798da2436a2SJaroslav Kysela case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
7991da177e4SLinus Torvalds val = ICH_IOCE | ICH_STARTBM;
800da2436a2SJaroslav Kysela ichdev->last_pos = ichdev->position;
8011da177e4SLinus Torvalds break;
8021da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_SUSPEND:
8031cfe43d2STakashi Iwai ichdev->suspended = 1;
804c0dbbdadSGustavo A. R. Silva fallthrough;
8051cfe43d2STakashi Iwai case SNDRV_PCM_TRIGGER_STOP:
8061da177e4SLinus Torvalds val = 0;
8071da177e4SLinus Torvalds break;
8081da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
8091da177e4SLinus Torvalds val = ICH_IOCE;
8101da177e4SLinus Torvalds break;
8111da177e4SLinus Torvalds default:
8121da177e4SLinus Torvalds return -EINVAL;
8131da177e4SLinus Torvalds }
8141da177e4SLinus Torvalds iputbyte(chip, port + ICH_REG_OFF_CR, val);
8151da177e4SLinus Torvalds if (cmd == SNDRV_PCM_TRIGGER_STOP) {
8161da177e4SLinus Torvalds /* wait until DMA stopped */
8171da177e4SLinus Torvalds while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH)) ;
8181da177e4SLinus Torvalds /* reset whole DMA things */
8191da177e4SLinus Torvalds iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
8201da177e4SLinus Torvalds }
8211da177e4SLinus Torvalds return 0;
8221da177e4SLinus Torvalds }
8231da177e4SLinus Torvalds
snd_intel8x0_ali_trigger(struct snd_pcm_substream * substream,int cmd)8246b75a9d8STakashi Iwai static int snd_intel8x0_ali_trigger(struct snd_pcm_substream *substream, int cmd)
8251da177e4SLinus Torvalds {
8266b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
8276b75a9d8STakashi Iwai struct ichdev *ichdev = get_ichdev(substream);
8281da177e4SLinus Torvalds unsigned long port = ichdev->reg_offset;
829f729f88aSTakashi Iwai static const int fiforeg[] = {
8306b75a9d8STakashi Iwai ICHREG(ALI_FIFOCR1), ICHREG(ALI_FIFOCR2), ICHREG(ALI_FIFOCR3)
8316b75a9d8STakashi Iwai };
8321da177e4SLinus Torvalds unsigned int val, fifo;
8331da177e4SLinus Torvalds
8341da177e4SLinus Torvalds val = igetdword(chip, ICHREG(ALI_DMACR));
8351da177e4SLinus Torvalds switch (cmd) {
8361cfe43d2STakashi Iwai case SNDRV_PCM_TRIGGER_RESUME:
8371cfe43d2STakashi Iwai ichdev->suspended = 0;
838c0dbbdadSGustavo A. R. Silva fallthrough;
8391da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_START:
8401da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
8411da177e4SLinus Torvalds if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
8421da177e4SLinus Torvalds /* clear FIFO for synchronization of channels */
8431da177e4SLinus Torvalds fifo = igetdword(chip, fiforeg[ichdev->ali_slot / 4]);
8441da177e4SLinus Torvalds fifo &= ~(0xff << (ichdev->ali_slot % 4));
8451da177e4SLinus Torvalds fifo |= 0x83 << (ichdev->ali_slot % 4);
8461da177e4SLinus Torvalds iputdword(chip, fiforeg[ichdev->ali_slot / 4], fifo);
8471da177e4SLinus Torvalds }
8481da177e4SLinus Torvalds iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
8491da177e4SLinus Torvalds val &= ~(1 << (ichdev->ali_slot + 16)); /* clear PAUSE flag */
8506b75a9d8STakashi Iwai /* start DMA */
8516b75a9d8STakashi Iwai iputdword(chip, ICHREG(ALI_DMACR), val | (1 << ichdev->ali_slot));
8521da177e4SLinus Torvalds break;
8531cfe43d2STakashi Iwai case SNDRV_PCM_TRIGGER_SUSPEND:
8541cfe43d2STakashi Iwai ichdev->suspended = 1;
855c0dbbdadSGustavo A. R. Silva fallthrough;
8561da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_STOP:
8571da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
8586b75a9d8STakashi Iwai /* pause */
8596b75a9d8STakashi Iwai iputdword(chip, ICHREG(ALI_DMACR), val | (1 << (ichdev->ali_slot + 16)));
8601da177e4SLinus Torvalds iputbyte(chip, port + ICH_REG_OFF_CR, 0);
8611da177e4SLinus Torvalds while (igetbyte(chip, port + ICH_REG_OFF_CR))
8621da177e4SLinus Torvalds ;
8631da177e4SLinus Torvalds if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
8641da177e4SLinus Torvalds break;
8651da177e4SLinus Torvalds /* reset whole DMA things */
8661da177e4SLinus Torvalds iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
8671da177e4SLinus Torvalds /* clear interrupts */
8686b75a9d8STakashi Iwai iputbyte(chip, port + ICH_REG_OFF_SR,
8696b75a9d8STakashi Iwai igetbyte(chip, port + ICH_REG_OFF_SR) | 0x1e);
8701da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_INTERRUPTSR),
8711da177e4SLinus Torvalds igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & ichdev->int_sta_mask);
8721da177e4SLinus Torvalds break;
8731da177e4SLinus Torvalds default:
8741da177e4SLinus Torvalds return -EINVAL;
8751da177e4SLinus Torvalds }
8761da177e4SLinus Torvalds return 0;
8771da177e4SLinus Torvalds }
8781da177e4SLinus Torvalds
snd_intel8x0_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)8796b75a9d8STakashi Iwai static int snd_intel8x0_hw_params(struct snd_pcm_substream *substream,
8806b75a9d8STakashi Iwai struct snd_pcm_hw_params *hw_params)
8811da177e4SLinus Torvalds {
8826b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
8836b75a9d8STakashi Iwai struct ichdev *ichdev = get_ichdev(substream);
8841da177e4SLinus Torvalds int dbl = params_rate(hw_params) > 48000;
8851da177e4SLinus Torvalds int err;
8861da177e4SLinus Torvalds
8871da177e4SLinus Torvalds if (ichdev->pcm_open_flag) {
8881da177e4SLinus Torvalds snd_ac97_pcm_close(ichdev->pcm);
8891da177e4SLinus Torvalds ichdev->pcm_open_flag = 0;
890c1f06161STakashi Iwai ichdev->prepared = 0;
8911da177e4SLinus Torvalds }
8921da177e4SLinus Torvalds err = snd_ac97_pcm_open(ichdev->pcm, params_rate(hw_params),
8931da177e4SLinus Torvalds params_channels(hw_params),
8941da177e4SLinus Torvalds ichdev->pcm->r[dbl].slots);
8951da177e4SLinus Torvalds if (err >= 0) {
8961da177e4SLinus Torvalds ichdev->pcm_open_flag = 1;
8971da177e4SLinus Torvalds /* Force SPDIF setting */
8981da177e4SLinus Torvalds if (ichdev->ichd == ICHD_PCMOUT && chip->spdif_idx < 0)
8996b75a9d8STakashi Iwai snd_ac97_set_rate(ichdev->pcm->r[0].codec[0], AC97_SPDIF,
9006b75a9d8STakashi Iwai params_rate(hw_params));
9011da177e4SLinus Torvalds }
9021da177e4SLinus Torvalds return err;
9031da177e4SLinus Torvalds }
9041da177e4SLinus Torvalds
snd_intel8x0_hw_free(struct snd_pcm_substream * substream)9056b75a9d8STakashi Iwai static int snd_intel8x0_hw_free(struct snd_pcm_substream *substream)
9061da177e4SLinus Torvalds {
9076b75a9d8STakashi Iwai struct ichdev *ichdev = get_ichdev(substream);
9081da177e4SLinus Torvalds
9091da177e4SLinus Torvalds if (ichdev->pcm_open_flag) {
9101da177e4SLinus Torvalds snd_ac97_pcm_close(ichdev->pcm);
9111da177e4SLinus Torvalds ichdev->pcm_open_flag = 0;
912c1f06161STakashi Iwai ichdev->prepared = 0;
9131da177e4SLinus Torvalds }
91428d52aa5STakashi Iwai return 0;
9151da177e4SLinus Torvalds }
9161da177e4SLinus Torvalds
snd_intel8x0_setup_pcm_out(struct intel8x0 * chip,struct snd_pcm_runtime * runtime)9176b75a9d8STakashi Iwai static void snd_intel8x0_setup_pcm_out(struct intel8x0 *chip,
9186b75a9d8STakashi Iwai struct snd_pcm_runtime *runtime)
9191da177e4SLinus Torvalds {
9201da177e4SLinus Torvalds unsigned int cnt;
9211da177e4SLinus Torvalds int dbl = runtime->rate > 48000;
9221cfe43d2STakashi Iwai
9231cfe43d2STakashi Iwai spin_lock_irq(&chip->reg_lock);
9241da177e4SLinus Torvalds switch (chip->device_type) {
9251da177e4SLinus Torvalds case DEVICE_ALI:
9261da177e4SLinus Torvalds cnt = igetdword(chip, ICHREG(ALI_SCR));
9271da177e4SLinus Torvalds cnt &= ~ICH_ALI_SC_PCM_246_MASK;
9281da177e4SLinus Torvalds if (runtime->channels == 4 || dbl)
9291da177e4SLinus Torvalds cnt |= ICH_ALI_SC_PCM_4;
9301da177e4SLinus Torvalds else if (runtime->channels == 6)
9311da177e4SLinus Torvalds cnt |= ICH_ALI_SC_PCM_6;
9321da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_SCR), cnt);
9331da177e4SLinus Torvalds break;
9341da177e4SLinus Torvalds case DEVICE_SIS:
9351da177e4SLinus Torvalds cnt = igetdword(chip, ICHREG(GLOB_CNT));
9361da177e4SLinus Torvalds cnt &= ~ICH_SIS_PCM_246_MASK;
9371da177e4SLinus Torvalds if (runtime->channels == 4 || dbl)
9381da177e4SLinus Torvalds cnt |= ICH_SIS_PCM_4;
9391da177e4SLinus Torvalds else if (runtime->channels == 6)
9401da177e4SLinus Torvalds cnt |= ICH_SIS_PCM_6;
9411da177e4SLinus Torvalds iputdword(chip, ICHREG(GLOB_CNT), cnt);
9421da177e4SLinus Torvalds break;
9431da177e4SLinus Torvalds default:
9441da177e4SLinus Torvalds cnt = igetdword(chip, ICHREG(GLOB_CNT));
9451da177e4SLinus Torvalds cnt &= ~(ICH_PCM_246_MASK | ICH_PCM_20BIT);
9461da177e4SLinus Torvalds if (runtime->channels == 4 || dbl)
9471da177e4SLinus Torvalds cnt |= ICH_PCM_4;
9481da177e4SLinus Torvalds else if (runtime->channels == 6)
9491da177e4SLinus Torvalds cnt |= ICH_PCM_6;
9504235a317STakashi Iwai else if (runtime->channels == 8)
9514235a317STakashi Iwai cnt |= ICH_PCM_8;
9521da177e4SLinus Torvalds if (chip->device_type == DEVICE_NFORCE) {
9531da177e4SLinus Torvalds /* reset to 2ch once to keep the 6 channel data in alignment,
9541da177e4SLinus Torvalds * to start from Front Left always
9551da177e4SLinus Torvalds */
9561da177e4SLinus Torvalds if (cnt & ICH_PCM_246_MASK) {
9571da177e4SLinus Torvalds iputdword(chip, ICHREG(GLOB_CNT), cnt & ~ICH_PCM_246_MASK);
9581da177e4SLinus Torvalds spin_unlock_irq(&chip->reg_lock);
9591da177e4SLinus Torvalds msleep(50); /* grrr... */
9601da177e4SLinus Torvalds spin_lock_irq(&chip->reg_lock);
9611da177e4SLinus Torvalds }
9621da177e4SLinus Torvalds } else if (chip->device_type == DEVICE_INTEL_ICH4) {
9631da177e4SLinus Torvalds if (runtime->sample_bits > 16)
9641da177e4SLinus Torvalds cnt |= ICH_PCM_20BIT;
9651da177e4SLinus Torvalds }
9661da177e4SLinus Torvalds iputdword(chip, ICHREG(GLOB_CNT), cnt);
9671da177e4SLinus Torvalds break;
9681da177e4SLinus Torvalds }
9691cfe43d2STakashi Iwai spin_unlock_irq(&chip->reg_lock);
9701da177e4SLinus Torvalds }
9711da177e4SLinus Torvalds
snd_intel8x0_pcm_prepare(struct snd_pcm_substream * substream)9726b75a9d8STakashi Iwai static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream)
9731da177e4SLinus Torvalds {
9746b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
9756b75a9d8STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
9766b75a9d8STakashi Iwai struct ichdev *ichdev = get_ichdev(substream);
9771da177e4SLinus Torvalds
9781da177e4SLinus Torvalds ichdev->physbuf = runtime->dma_addr;
9791da177e4SLinus Torvalds ichdev->size = snd_pcm_lib_buffer_bytes(substream);
9801da177e4SLinus Torvalds ichdev->fragsize = snd_pcm_lib_period_bytes(substream);
9811da177e4SLinus Torvalds if (ichdev->ichd == ICHD_PCMOUT) {
9821da177e4SLinus Torvalds snd_intel8x0_setup_pcm_out(chip, runtime);
9831cfe43d2STakashi Iwai if (chip->device_type == DEVICE_INTEL_ICH4)
9841da177e4SLinus Torvalds ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;
9851da177e4SLinus Torvalds }
9861da177e4SLinus Torvalds snd_intel8x0_setup_periods(chip, ichdev);
987c1f06161STakashi Iwai ichdev->prepared = 1;
9881da177e4SLinus Torvalds return 0;
9891da177e4SLinus Torvalds }
9901da177e4SLinus Torvalds
snd_intel8x0_pcm_pointer(struct snd_pcm_substream * substream)9916b75a9d8STakashi Iwai static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *substream)
9921da177e4SLinus Torvalds {
9936b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
9946b75a9d8STakashi Iwai struct ichdev *ichdev = get_ichdev(substream);
9951da177e4SLinus Torvalds size_t ptr1, ptr;
996da2436a2SJaroslav Kysela int civ, timeout = 10;
9971da177e4SLinus Torvalds unsigned int position;
9981da177e4SLinus Torvalds
9991da177e4SLinus Torvalds spin_lock(&chip->reg_lock);
10001da177e4SLinus Torvalds do {
10011da177e4SLinus Torvalds civ = igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV);
10021da177e4SLinus Torvalds ptr1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
10031da177e4SLinus Torvalds position = ichdev->position;
10041da177e4SLinus Torvalds if (ptr1 == 0) {
10051da177e4SLinus Torvalds udelay(10);
10061da177e4SLinus Torvalds continue;
10071da177e4SLinus Torvalds }
1008228cf793SKonstantin Ozerkov if (civ != igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV))
1009228cf793SKonstantin Ozerkov continue;
10105a9a5179SDenis V. Lunev
10115a9a5179SDenis V. Lunev /* IO read operation is very expensive inside virtual machine
10125a9a5179SDenis V. Lunev * as it is emulated. The probability that subsequent PICB read
10135a9a5179SDenis V. Lunev * will return different result is high enough to loop till
10145a9a5179SDenis V. Lunev * timeout here.
10155a9a5179SDenis V. Lunev * Same CIV is strict enough condition to be sure that PICB
10165a9a5179SDenis V. Lunev * is valid inside VM on emulated card. */
1017228cf793SKonstantin Ozerkov if (chip->inside_vm)
1018228cf793SKonstantin Ozerkov break;
1019228cf793SKonstantin Ozerkov if (ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
10201da177e4SLinus Torvalds break;
10211da177e4SLinus Torvalds } while (timeout--);
1022f708eb1dSTakashi Iwai ptr = ichdev->last_pos;
1023da2436a2SJaroslav Kysela if (ptr1 != 0) {
10241da177e4SLinus Torvalds ptr1 <<= ichdev->pos_shift;
10251da177e4SLinus Torvalds ptr = ichdev->fragsize1 - ptr1;
10261da177e4SLinus Torvalds ptr += position;
1027f708eb1dSTakashi Iwai if (ptr < ichdev->last_pos) {
1028f708eb1dSTakashi Iwai unsigned int pos_base, last_base;
1029f708eb1dSTakashi Iwai pos_base = position / ichdev->fragsize1;
1030f708eb1dSTakashi Iwai last_base = ichdev->last_pos / ichdev->fragsize1;
1031f708eb1dSTakashi Iwai /* another sanity check; ptr1 can go back to full
1032f708eb1dSTakashi Iwai * before the base position is updated
1033f708eb1dSTakashi Iwai */
1034f708eb1dSTakashi Iwai if (pos_base == last_base)
1035f708eb1dSTakashi Iwai ptr = ichdev->last_pos;
1036da2436a2SJaroslav Kysela }
1037f708eb1dSTakashi Iwai }
1038f708eb1dSTakashi Iwai ichdev->last_pos = ptr;
10391da177e4SLinus Torvalds spin_unlock(&chip->reg_lock);
10401da177e4SLinus Torvalds if (ptr >= ichdev->size)
10411da177e4SLinus Torvalds return 0;
10421da177e4SLinus Torvalds return bytes_to_frames(substream->runtime, ptr);
10431da177e4SLinus Torvalds }
10441da177e4SLinus Torvalds
1045dee49895SBhumika Goyal static const struct snd_pcm_hardware snd_intel8x0_stream =
10461da177e4SLinus Torvalds {
10471da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
10481da177e4SLinus Torvalds SNDRV_PCM_INFO_BLOCK_TRANSFER |
10491da177e4SLinus Torvalds SNDRV_PCM_INFO_MMAP_VALID |
10501da177e4SLinus Torvalds SNDRV_PCM_INFO_PAUSE |
10511da177e4SLinus Torvalds SNDRV_PCM_INFO_RESUME),
10521da177e4SLinus Torvalds .formats = SNDRV_PCM_FMTBIT_S16_LE,
10531da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_48000,
10541da177e4SLinus Torvalds .rate_min = 48000,
10551da177e4SLinus Torvalds .rate_max = 48000,
10561da177e4SLinus Torvalds .channels_min = 2,
10571da177e4SLinus Torvalds .channels_max = 2,
10581da177e4SLinus Torvalds .buffer_bytes_max = 128 * 1024,
10591da177e4SLinus Torvalds .period_bytes_min = 32,
10601da177e4SLinus Torvalds .period_bytes_max = 128 * 1024,
10611da177e4SLinus Torvalds .periods_min = 1,
10621da177e4SLinus Torvalds .periods_max = 1024,
10631da177e4SLinus Torvalds .fifo_size = 0,
10641da177e4SLinus Torvalds };
10651da177e4SLinus Torvalds
10660f470ce6STakashi Iwai static const unsigned int channels4[] = {
10671da177e4SLinus Torvalds 2, 4,
10681da177e4SLinus Torvalds };
10691da177e4SLinus Torvalds
10700f470ce6STakashi Iwai static const struct snd_pcm_hw_constraint_list hw_constraints_channels4 = {
10711da177e4SLinus Torvalds .count = ARRAY_SIZE(channels4),
10721da177e4SLinus Torvalds .list = channels4,
10731da177e4SLinus Torvalds .mask = 0,
10741da177e4SLinus Torvalds };
10751da177e4SLinus Torvalds
10760f470ce6STakashi Iwai static const unsigned int channels6[] = {
10771da177e4SLinus Torvalds 2, 4, 6,
10781da177e4SLinus Torvalds };
10791da177e4SLinus Torvalds
10800f470ce6STakashi Iwai static const struct snd_pcm_hw_constraint_list hw_constraints_channels6 = {
10811da177e4SLinus Torvalds .count = ARRAY_SIZE(channels6),
10821da177e4SLinus Torvalds .list = channels6,
10831da177e4SLinus Torvalds .mask = 0,
10841da177e4SLinus Torvalds };
10851da177e4SLinus Torvalds
10860f470ce6STakashi Iwai static const unsigned int channels8[] = {
10874235a317STakashi Iwai 2, 4, 6, 8,
10884235a317STakashi Iwai };
10894235a317STakashi Iwai
10900f470ce6STakashi Iwai static const struct snd_pcm_hw_constraint_list hw_constraints_channels8 = {
10914235a317STakashi Iwai .count = ARRAY_SIZE(channels8),
10924235a317STakashi Iwai .list = channels8,
10934235a317STakashi Iwai .mask = 0,
10944235a317STakashi Iwai };
10954235a317STakashi Iwai
snd_intel8x0_pcm_open(struct snd_pcm_substream * substream,struct ichdev * ichdev)10966b75a9d8STakashi Iwai static int snd_intel8x0_pcm_open(struct snd_pcm_substream *substream, struct ichdev *ichdev)
10971da177e4SLinus Torvalds {
10986b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
10996b75a9d8STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
11001da177e4SLinus Torvalds int err;
11011da177e4SLinus Torvalds
11021da177e4SLinus Torvalds ichdev->substream = substream;
11031da177e4SLinus Torvalds runtime->hw = snd_intel8x0_stream;
11041da177e4SLinus Torvalds runtime->hw.rates = ichdev->pcm->rates;
11051da177e4SLinus Torvalds snd_pcm_limit_hw_rates(runtime);
11061da177e4SLinus Torvalds if (chip->device_type == DEVICE_SIS) {
11071da177e4SLinus Torvalds runtime->hw.buffer_bytes_max = 64*1024;
11081da177e4SLinus Torvalds runtime->hw.period_bytes_max = 64*1024;
11091da177e4SLinus Torvalds }
11103a5f3dd3STakashi Iwai err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
11113a5f3dd3STakashi Iwai if (err < 0)
11121da177e4SLinus Torvalds return err;
11131da177e4SLinus Torvalds runtime->private_data = ichdev;
11141da177e4SLinus Torvalds return 0;
11151da177e4SLinus Torvalds }
11161da177e4SLinus Torvalds
snd_intel8x0_playback_open(struct snd_pcm_substream * substream)11176b75a9d8STakashi Iwai static int snd_intel8x0_playback_open(struct snd_pcm_substream *substream)
11181da177e4SLinus Torvalds {
11196b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
11206b75a9d8STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime;
11211da177e4SLinus Torvalds int err;
11221da177e4SLinus Torvalds
11231da177e4SLinus Torvalds err = snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCMOUT]);
11241da177e4SLinus Torvalds if (err < 0)
11251da177e4SLinus Torvalds return err;
11261da177e4SLinus Torvalds
11274235a317STakashi Iwai if (chip->multi8) {
11284235a317STakashi Iwai runtime->hw.channels_max = 8;
11294235a317STakashi Iwai snd_pcm_hw_constraint_list(runtime, 0,
11304235a317STakashi Iwai SNDRV_PCM_HW_PARAM_CHANNELS,
11314235a317STakashi Iwai &hw_constraints_channels8);
11324235a317STakashi Iwai } else if (chip->multi6) {
11331da177e4SLinus Torvalds runtime->hw.channels_max = 6;
11346b75a9d8STakashi Iwai snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
11356b75a9d8STakashi Iwai &hw_constraints_channels6);
11361da177e4SLinus Torvalds } else if (chip->multi4) {
11371da177e4SLinus Torvalds runtime->hw.channels_max = 4;
11386b75a9d8STakashi Iwai snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
11396b75a9d8STakashi Iwai &hw_constraints_channels4);
11401da177e4SLinus Torvalds }
11411da177e4SLinus Torvalds if (chip->dra) {
11421da177e4SLinus Torvalds snd_ac97_pcm_double_rate_rules(runtime);
11431da177e4SLinus Torvalds }
11441da177e4SLinus Torvalds if (chip->smp20bit) {
11451da177e4SLinus Torvalds runtime->hw.formats |= SNDRV_PCM_FMTBIT_S32_LE;
11461da177e4SLinus Torvalds snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20);
11471da177e4SLinus Torvalds }
11481da177e4SLinus Torvalds return 0;
11491da177e4SLinus Torvalds }
11501da177e4SLinus Torvalds
snd_intel8x0_playback_close(struct snd_pcm_substream * substream)11516b75a9d8STakashi Iwai static int snd_intel8x0_playback_close(struct snd_pcm_substream *substream)
11521da177e4SLinus Torvalds {
11536b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
11541da177e4SLinus Torvalds
11551da177e4SLinus Torvalds chip->ichd[ICHD_PCMOUT].substream = NULL;
11561da177e4SLinus Torvalds return 0;
11571da177e4SLinus Torvalds }
11581da177e4SLinus Torvalds
snd_intel8x0_capture_open(struct snd_pcm_substream * substream)11596b75a9d8STakashi Iwai static int snd_intel8x0_capture_open(struct snd_pcm_substream *substream)
11601da177e4SLinus Torvalds {
11616b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
11621da177e4SLinus Torvalds
11631da177e4SLinus Torvalds return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCMIN]);
11641da177e4SLinus Torvalds }
11651da177e4SLinus Torvalds
snd_intel8x0_capture_close(struct snd_pcm_substream * substream)11666b75a9d8STakashi Iwai static int snd_intel8x0_capture_close(struct snd_pcm_substream *substream)
11671da177e4SLinus Torvalds {
11686b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
11691da177e4SLinus Torvalds
11701da177e4SLinus Torvalds chip->ichd[ICHD_PCMIN].substream = NULL;
11711da177e4SLinus Torvalds return 0;
11721da177e4SLinus Torvalds }
11731da177e4SLinus Torvalds
snd_intel8x0_mic_open(struct snd_pcm_substream * substream)11746b75a9d8STakashi Iwai static int snd_intel8x0_mic_open(struct snd_pcm_substream *substream)
11751da177e4SLinus Torvalds {
11766b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
11771da177e4SLinus Torvalds
11781da177e4SLinus Torvalds return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_MIC]);
11791da177e4SLinus Torvalds }
11801da177e4SLinus Torvalds
snd_intel8x0_mic_close(struct snd_pcm_substream * substream)11816b75a9d8STakashi Iwai static int snd_intel8x0_mic_close(struct snd_pcm_substream *substream)
11821da177e4SLinus Torvalds {
11836b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
11841da177e4SLinus Torvalds
11851da177e4SLinus Torvalds chip->ichd[ICHD_MIC].substream = NULL;
11861da177e4SLinus Torvalds return 0;
11871da177e4SLinus Torvalds }
11881da177e4SLinus Torvalds
snd_intel8x0_mic2_open(struct snd_pcm_substream * substream)11896b75a9d8STakashi Iwai static int snd_intel8x0_mic2_open(struct snd_pcm_substream *substream)
11901da177e4SLinus Torvalds {
11916b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
11921da177e4SLinus Torvalds
11931da177e4SLinus Torvalds return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_MIC2]);
11941da177e4SLinus Torvalds }
11951da177e4SLinus Torvalds
snd_intel8x0_mic2_close(struct snd_pcm_substream * substream)11966b75a9d8STakashi Iwai static int snd_intel8x0_mic2_close(struct snd_pcm_substream *substream)
11971da177e4SLinus Torvalds {
11986b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
11991da177e4SLinus Torvalds
12001da177e4SLinus Torvalds chip->ichd[ICHD_MIC2].substream = NULL;
12011da177e4SLinus Torvalds return 0;
12021da177e4SLinus Torvalds }
12031da177e4SLinus Torvalds
snd_intel8x0_capture2_open(struct snd_pcm_substream * substream)12046b75a9d8STakashi Iwai static int snd_intel8x0_capture2_open(struct snd_pcm_substream *substream)
12051da177e4SLinus Torvalds {
12066b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
12071da177e4SLinus Torvalds
12081da177e4SLinus Torvalds return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCM2IN]);
12091da177e4SLinus Torvalds }
12101da177e4SLinus Torvalds
snd_intel8x0_capture2_close(struct snd_pcm_substream * substream)12116b75a9d8STakashi Iwai static int snd_intel8x0_capture2_close(struct snd_pcm_substream *substream)
12121da177e4SLinus Torvalds {
12136b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
12141da177e4SLinus Torvalds
12151da177e4SLinus Torvalds chip->ichd[ICHD_PCM2IN].substream = NULL;
12161da177e4SLinus Torvalds return 0;
12171da177e4SLinus Torvalds }
12181da177e4SLinus Torvalds
snd_intel8x0_spdif_open(struct snd_pcm_substream * substream)12196b75a9d8STakashi Iwai static int snd_intel8x0_spdif_open(struct snd_pcm_substream *substream)
12201da177e4SLinus Torvalds {
12216b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
12221da177e4SLinus Torvalds int idx = chip->device_type == DEVICE_NFORCE ? NVD_SPBAR : ICHD_SPBAR;
12231da177e4SLinus Torvalds
12241da177e4SLinus Torvalds return snd_intel8x0_pcm_open(substream, &chip->ichd[idx]);
12251da177e4SLinus Torvalds }
12261da177e4SLinus Torvalds
snd_intel8x0_spdif_close(struct snd_pcm_substream * substream)12276b75a9d8STakashi Iwai static int snd_intel8x0_spdif_close(struct snd_pcm_substream *substream)
12281da177e4SLinus Torvalds {
12296b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
12301da177e4SLinus Torvalds int idx = chip->device_type == DEVICE_NFORCE ? NVD_SPBAR : ICHD_SPBAR;
12311da177e4SLinus Torvalds
12321da177e4SLinus Torvalds chip->ichd[idx].substream = NULL;
12331da177e4SLinus Torvalds return 0;
12341da177e4SLinus Torvalds }
12351da177e4SLinus Torvalds
snd_intel8x0_ali_ac97spdifout_open(struct snd_pcm_substream * substream)12366b75a9d8STakashi Iwai static int snd_intel8x0_ali_ac97spdifout_open(struct snd_pcm_substream *substream)
12371da177e4SLinus Torvalds {
12386b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
12391da177e4SLinus Torvalds unsigned int val;
12401da177e4SLinus Torvalds
12411da177e4SLinus Torvalds spin_lock_irq(&chip->reg_lock);
12421da177e4SLinus Torvalds val = igetdword(chip, ICHREG(ALI_INTERFACECR));
12431da177e4SLinus Torvalds val |= ICH_ALI_IF_AC97SP;
12441da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_INTERFACECR), val);
12451da177e4SLinus Torvalds /* also needs to set ALI_SC_CODEC_SPDF correctly */
12461da177e4SLinus Torvalds spin_unlock_irq(&chip->reg_lock);
12471da177e4SLinus Torvalds
12481da177e4SLinus Torvalds return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_AC97SPDIFOUT]);
12491da177e4SLinus Torvalds }
12501da177e4SLinus Torvalds
snd_intel8x0_ali_ac97spdifout_close(struct snd_pcm_substream * substream)12516b75a9d8STakashi Iwai static int snd_intel8x0_ali_ac97spdifout_close(struct snd_pcm_substream *substream)
12521da177e4SLinus Torvalds {
12536b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
12541da177e4SLinus Torvalds unsigned int val;
12551da177e4SLinus Torvalds
12561da177e4SLinus Torvalds chip->ichd[ALID_AC97SPDIFOUT].substream = NULL;
12571da177e4SLinus Torvalds spin_lock_irq(&chip->reg_lock);
12581da177e4SLinus Torvalds val = igetdword(chip, ICHREG(ALI_INTERFACECR));
12591da177e4SLinus Torvalds val &= ~ICH_ALI_IF_AC97SP;
12601da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_INTERFACECR), val);
12611da177e4SLinus Torvalds spin_unlock_irq(&chip->reg_lock);
12621da177e4SLinus Torvalds
12631da177e4SLinus Torvalds return 0;
12641da177e4SLinus Torvalds }
12651da177e4SLinus Torvalds
12661a183131STakashi Iwai #if 0 // NYI
12676b75a9d8STakashi Iwai static int snd_intel8x0_ali_spdifin_open(struct snd_pcm_substream *substream)
12681da177e4SLinus Torvalds {
12696b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
12701da177e4SLinus Torvalds
12711da177e4SLinus Torvalds return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_SPDIFIN]);
12721da177e4SLinus Torvalds }
12731da177e4SLinus Torvalds
12746b75a9d8STakashi Iwai static int snd_intel8x0_ali_spdifin_close(struct snd_pcm_substream *substream)
12751da177e4SLinus Torvalds {
12766b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
12771da177e4SLinus Torvalds
12781da177e4SLinus Torvalds chip->ichd[ALID_SPDIFIN].substream = NULL;
12791da177e4SLinus Torvalds return 0;
12801da177e4SLinus Torvalds }
12811da177e4SLinus Torvalds
12826b75a9d8STakashi Iwai static int snd_intel8x0_ali_spdifout_open(struct snd_pcm_substream *substream)
12831da177e4SLinus Torvalds {
12846b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
12851da177e4SLinus Torvalds
12861da177e4SLinus Torvalds return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_SPDIFOUT]);
12871da177e4SLinus Torvalds }
12881da177e4SLinus Torvalds
12896b75a9d8STakashi Iwai static int snd_intel8x0_ali_spdifout_close(struct snd_pcm_substream *substream)
12901da177e4SLinus Torvalds {
12916b75a9d8STakashi Iwai struct intel8x0 *chip = snd_pcm_substream_chip(substream);
12921da177e4SLinus Torvalds
12931da177e4SLinus Torvalds chip->ichd[ALID_SPDIFOUT].substream = NULL;
12941da177e4SLinus Torvalds return 0;
12951da177e4SLinus Torvalds }
12961da177e4SLinus Torvalds #endif
12971da177e4SLinus Torvalds
129817e12921SArvind Yadav static const struct snd_pcm_ops snd_intel8x0_playback_ops = {
12991da177e4SLinus Torvalds .open = snd_intel8x0_playback_open,
13001da177e4SLinus Torvalds .close = snd_intel8x0_playback_close,
13011da177e4SLinus Torvalds .hw_params = snd_intel8x0_hw_params,
13021da177e4SLinus Torvalds .hw_free = snd_intel8x0_hw_free,
13031da177e4SLinus Torvalds .prepare = snd_intel8x0_pcm_prepare,
13041da177e4SLinus Torvalds .trigger = snd_intel8x0_pcm_trigger,
13051da177e4SLinus Torvalds .pointer = snd_intel8x0_pcm_pointer,
13061da177e4SLinus Torvalds };
13071da177e4SLinus Torvalds
130817e12921SArvind Yadav static const struct snd_pcm_ops snd_intel8x0_capture_ops = {
13091da177e4SLinus Torvalds .open = snd_intel8x0_capture_open,
13101da177e4SLinus Torvalds .close = snd_intel8x0_capture_close,
13111da177e4SLinus Torvalds .hw_params = snd_intel8x0_hw_params,
13121da177e4SLinus Torvalds .hw_free = snd_intel8x0_hw_free,
13131da177e4SLinus Torvalds .prepare = snd_intel8x0_pcm_prepare,
13141da177e4SLinus Torvalds .trigger = snd_intel8x0_pcm_trigger,
13151da177e4SLinus Torvalds .pointer = snd_intel8x0_pcm_pointer,
13161da177e4SLinus Torvalds };
13171da177e4SLinus Torvalds
131817e12921SArvind Yadav static const struct snd_pcm_ops snd_intel8x0_capture_mic_ops = {
13191da177e4SLinus Torvalds .open = snd_intel8x0_mic_open,
13201da177e4SLinus Torvalds .close = snd_intel8x0_mic_close,
13211da177e4SLinus Torvalds .hw_params = snd_intel8x0_hw_params,
13221da177e4SLinus Torvalds .hw_free = snd_intel8x0_hw_free,
13231da177e4SLinus Torvalds .prepare = snd_intel8x0_pcm_prepare,
13241da177e4SLinus Torvalds .trigger = snd_intel8x0_pcm_trigger,
13251da177e4SLinus Torvalds .pointer = snd_intel8x0_pcm_pointer,
13261da177e4SLinus Torvalds };
13271da177e4SLinus Torvalds
132817e12921SArvind Yadav static const struct snd_pcm_ops snd_intel8x0_capture_mic2_ops = {
13291da177e4SLinus Torvalds .open = snd_intel8x0_mic2_open,
13301da177e4SLinus Torvalds .close = snd_intel8x0_mic2_close,
13311da177e4SLinus Torvalds .hw_params = snd_intel8x0_hw_params,
13321da177e4SLinus Torvalds .hw_free = snd_intel8x0_hw_free,
13331da177e4SLinus Torvalds .prepare = snd_intel8x0_pcm_prepare,
13341da177e4SLinus Torvalds .trigger = snd_intel8x0_pcm_trigger,
13351da177e4SLinus Torvalds .pointer = snd_intel8x0_pcm_pointer,
13361da177e4SLinus Torvalds };
13371da177e4SLinus Torvalds
133817e12921SArvind Yadav static const struct snd_pcm_ops snd_intel8x0_capture2_ops = {
13391da177e4SLinus Torvalds .open = snd_intel8x0_capture2_open,
13401da177e4SLinus Torvalds .close = snd_intel8x0_capture2_close,
13411da177e4SLinus Torvalds .hw_params = snd_intel8x0_hw_params,
13421da177e4SLinus Torvalds .hw_free = snd_intel8x0_hw_free,
13431da177e4SLinus Torvalds .prepare = snd_intel8x0_pcm_prepare,
13441da177e4SLinus Torvalds .trigger = snd_intel8x0_pcm_trigger,
13451da177e4SLinus Torvalds .pointer = snd_intel8x0_pcm_pointer,
13461da177e4SLinus Torvalds };
13471da177e4SLinus Torvalds
134817e12921SArvind Yadav static const struct snd_pcm_ops snd_intel8x0_spdif_ops = {
13491da177e4SLinus Torvalds .open = snd_intel8x0_spdif_open,
13501da177e4SLinus Torvalds .close = snd_intel8x0_spdif_close,
13511da177e4SLinus Torvalds .hw_params = snd_intel8x0_hw_params,
13521da177e4SLinus Torvalds .hw_free = snd_intel8x0_hw_free,
13531da177e4SLinus Torvalds .prepare = snd_intel8x0_pcm_prepare,
13541da177e4SLinus Torvalds .trigger = snd_intel8x0_pcm_trigger,
13551da177e4SLinus Torvalds .pointer = snd_intel8x0_pcm_pointer,
13561da177e4SLinus Torvalds };
13571da177e4SLinus Torvalds
135817e12921SArvind Yadav static const struct snd_pcm_ops snd_intel8x0_ali_playback_ops = {
13591da177e4SLinus Torvalds .open = snd_intel8x0_playback_open,
13601da177e4SLinus Torvalds .close = snd_intel8x0_playback_close,
13611da177e4SLinus Torvalds .hw_params = snd_intel8x0_hw_params,
13621da177e4SLinus Torvalds .hw_free = snd_intel8x0_hw_free,
13631da177e4SLinus Torvalds .prepare = snd_intel8x0_pcm_prepare,
13641da177e4SLinus Torvalds .trigger = snd_intel8x0_ali_trigger,
13651da177e4SLinus Torvalds .pointer = snd_intel8x0_pcm_pointer,
13661da177e4SLinus Torvalds };
13671da177e4SLinus Torvalds
136817e12921SArvind Yadav static const struct snd_pcm_ops snd_intel8x0_ali_capture_ops = {
13691da177e4SLinus Torvalds .open = snd_intel8x0_capture_open,
13701da177e4SLinus Torvalds .close = snd_intel8x0_capture_close,
13711da177e4SLinus Torvalds .hw_params = snd_intel8x0_hw_params,
13721da177e4SLinus Torvalds .hw_free = snd_intel8x0_hw_free,
13731da177e4SLinus Torvalds .prepare = snd_intel8x0_pcm_prepare,
13741da177e4SLinus Torvalds .trigger = snd_intel8x0_ali_trigger,
13751da177e4SLinus Torvalds .pointer = snd_intel8x0_pcm_pointer,
13761da177e4SLinus Torvalds };
13771da177e4SLinus Torvalds
137817e12921SArvind Yadav static const struct snd_pcm_ops snd_intel8x0_ali_capture_mic_ops = {
13791da177e4SLinus Torvalds .open = snd_intel8x0_mic_open,
13801da177e4SLinus Torvalds .close = snd_intel8x0_mic_close,
13811da177e4SLinus Torvalds .hw_params = snd_intel8x0_hw_params,
13821da177e4SLinus Torvalds .hw_free = snd_intel8x0_hw_free,
13831da177e4SLinus Torvalds .prepare = snd_intel8x0_pcm_prepare,
13841da177e4SLinus Torvalds .trigger = snd_intel8x0_ali_trigger,
13851da177e4SLinus Torvalds .pointer = snd_intel8x0_pcm_pointer,
13861da177e4SLinus Torvalds };
13871da177e4SLinus Torvalds
138817e12921SArvind Yadav static const struct snd_pcm_ops snd_intel8x0_ali_ac97spdifout_ops = {
13891da177e4SLinus Torvalds .open = snd_intel8x0_ali_ac97spdifout_open,
13901da177e4SLinus Torvalds .close = snd_intel8x0_ali_ac97spdifout_close,
13911da177e4SLinus Torvalds .hw_params = snd_intel8x0_hw_params,
13921da177e4SLinus Torvalds .hw_free = snd_intel8x0_hw_free,
13931da177e4SLinus Torvalds .prepare = snd_intel8x0_pcm_prepare,
13941da177e4SLinus Torvalds .trigger = snd_intel8x0_ali_trigger,
13951da177e4SLinus Torvalds .pointer = snd_intel8x0_pcm_pointer,
13961da177e4SLinus Torvalds };
13971da177e4SLinus Torvalds
13981a183131STakashi Iwai #if 0 // NYI
13996b75a9d8STakashi Iwai static struct snd_pcm_ops snd_intel8x0_ali_spdifin_ops = {
14001da177e4SLinus Torvalds .open = snd_intel8x0_ali_spdifin_open,
14011da177e4SLinus Torvalds .close = snd_intel8x0_ali_spdifin_close,
14021da177e4SLinus Torvalds .hw_params = snd_intel8x0_hw_params,
14031da177e4SLinus Torvalds .hw_free = snd_intel8x0_hw_free,
14041da177e4SLinus Torvalds .prepare = snd_intel8x0_pcm_prepare,
14051da177e4SLinus Torvalds .trigger = snd_intel8x0_pcm_trigger,
14061da177e4SLinus Torvalds .pointer = snd_intel8x0_pcm_pointer,
14071da177e4SLinus Torvalds };
14081da177e4SLinus Torvalds
14096b75a9d8STakashi Iwai static struct snd_pcm_ops snd_intel8x0_ali_spdifout_ops = {
14101da177e4SLinus Torvalds .open = snd_intel8x0_ali_spdifout_open,
14111da177e4SLinus Torvalds .close = snd_intel8x0_ali_spdifout_close,
14121da177e4SLinus Torvalds .hw_params = snd_intel8x0_hw_params,
14131da177e4SLinus Torvalds .hw_free = snd_intel8x0_hw_free,
14141da177e4SLinus Torvalds .prepare = snd_intel8x0_pcm_prepare,
14151da177e4SLinus Torvalds .trigger = snd_intel8x0_pcm_trigger,
14161da177e4SLinus Torvalds .pointer = snd_intel8x0_pcm_pointer,
14171da177e4SLinus Torvalds };
14181da177e4SLinus Torvalds #endif // NYI
14191da177e4SLinus Torvalds
14201da177e4SLinus Torvalds struct ich_pcm_table {
14211da177e4SLinus Torvalds char *suffix;
142217e12921SArvind Yadav const struct snd_pcm_ops *playback_ops;
142317e12921SArvind Yadav const struct snd_pcm_ops *capture_ops;
14241da177e4SLinus Torvalds size_t prealloc_size;
14251da177e4SLinus Torvalds size_t prealloc_max_size;
14261da177e4SLinus Torvalds int ac97_idx;
14271da177e4SLinus Torvalds };
14281da177e4SLinus Torvalds
14294985ddbfSTakashi Iwai #define intel8x0_dma_type(chip) \
143058a95dfaSTakashi Iwai ((chip)->fix_nocache ? SNDRV_DMA_TYPE_DEV_WC : SNDRV_DMA_TYPE_DEV)
14314985ddbfSTakashi Iwai
snd_intel8x0_pcm1(struct intel8x0 * chip,int device,const struct ich_pcm_table * rec)1432e23e7a14SBill Pemberton static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device,
1433f729f88aSTakashi Iwai const struct ich_pcm_table *rec)
14341da177e4SLinus Torvalds {
14356b75a9d8STakashi Iwai struct snd_pcm *pcm;
14361da177e4SLinus Torvalds int err;
14371da177e4SLinus Torvalds char name[32];
14381da177e4SLinus Torvalds
14391da177e4SLinus Torvalds if (rec->suffix)
14401da177e4SLinus Torvalds sprintf(name, "Intel ICH - %s", rec->suffix);
14411da177e4SLinus Torvalds else
14421da177e4SLinus Torvalds strcpy(name, "Intel ICH");
14431da177e4SLinus Torvalds err = snd_pcm_new(chip->card, name, device,
14441da177e4SLinus Torvalds rec->playback_ops ? 1 : 0,
14451da177e4SLinus Torvalds rec->capture_ops ? 1 : 0, &pcm);
14461da177e4SLinus Torvalds if (err < 0)
14471da177e4SLinus Torvalds return err;
14481da177e4SLinus Torvalds
14491da177e4SLinus Torvalds if (rec->playback_ops)
14501da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, rec->playback_ops);
14511da177e4SLinus Torvalds if (rec->capture_ops)
14521da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, rec->capture_ops);
14531da177e4SLinus Torvalds
14541da177e4SLinus Torvalds pcm->private_data = chip;
14551da177e4SLinus Torvalds pcm->info_flags = 0;
14561da177e4SLinus Torvalds if (rec->suffix)
14571da177e4SLinus Torvalds sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix);
14581da177e4SLinus Torvalds else
14591da177e4SLinus Torvalds strcpy(pcm->name, chip->card->shortname);
14601da177e4SLinus Torvalds chip->pcm[device] = pcm;
14611da177e4SLinus Torvalds
146228d52aa5STakashi Iwai snd_pcm_set_managed_buffer_all(pcm, intel8x0_dma_type(chip),
14636974f8adSTakashi Iwai &chip->pci->dev,
14641da177e4SLinus Torvalds rec->prealloc_size, rec->prealloc_max_size);
14651da177e4SLinus Torvalds
1466791b3f59STakashi Iwai if (rec->playback_ops &&
1467791b3f59STakashi Iwai rec->playback_ops->open == snd_intel8x0_playback_open) {
1468e36e3b86STakashi Iwai struct snd_pcm_chmap *chmap;
1469e36e3b86STakashi Iwai int chs = 2;
1470e36e3b86STakashi Iwai if (chip->multi8)
1471e36e3b86STakashi Iwai chs = 8;
1472e36e3b86STakashi Iwai else if (chip->multi6)
1473e36e3b86STakashi Iwai chs = 6;
1474e36e3b86STakashi Iwai else if (chip->multi4)
1475e36e3b86STakashi Iwai chs = 4;
1476e36e3b86STakashi Iwai err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
1477e36e3b86STakashi Iwai snd_pcm_alt_chmaps, chs, 0,
1478e36e3b86STakashi Iwai &chmap);
1479e36e3b86STakashi Iwai if (err < 0)
1480e36e3b86STakashi Iwai return err;
1481e36e3b86STakashi Iwai chmap->channel_mask = SND_PCM_CHMAP_MASK_2468;
1482e36e3b86STakashi Iwai chip->ac97[0]->chmaps[SNDRV_PCM_STREAM_PLAYBACK] = chmap;
1483e36e3b86STakashi Iwai }
1484e36e3b86STakashi Iwai
14851da177e4SLinus Torvalds return 0;
14861da177e4SLinus Torvalds }
14871da177e4SLinus Torvalds
1488f729f88aSTakashi Iwai static const struct ich_pcm_table intel_pcms[] = {
14891da177e4SLinus Torvalds {
14901da177e4SLinus Torvalds .playback_ops = &snd_intel8x0_playback_ops,
14911da177e4SLinus Torvalds .capture_ops = &snd_intel8x0_capture_ops,
14921da177e4SLinus Torvalds .prealloc_size = 64 * 1024,
14931da177e4SLinus Torvalds .prealloc_max_size = 128 * 1024,
14941da177e4SLinus Torvalds },
14951da177e4SLinus Torvalds {
14961da177e4SLinus Torvalds .suffix = "MIC ADC",
14971da177e4SLinus Torvalds .capture_ops = &snd_intel8x0_capture_mic_ops,
14981da177e4SLinus Torvalds .prealloc_size = 0,
14991da177e4SLinus Torvalds .prealloc_max_size = 128 * 1024,
15001da177e4SLinus Torvalds .ac97_idx = ICHD_MIC,
15011da177e4SLinus Torvalds },
15021da177e4SLinus Torvalds {
15031da177e4SLinus Torvalds .suffix = "MIC2 ADC",
15041da177e4SLinus Torvalds .capture_ops = &snd_intel8x0_capture_mic2_ops,
15051da177e4SLinus Torvalds .prealloc_size = 0,
15061da177e4SLinus Torvalds .prealloc_max_size = 128 * 1024,
15071da177e4SLinus Torvalds .ac97_idx = ICHD_MIC2,
15081da177e4SLinus Torvalds },
15091da177e4SLinus Torvalds {
15101da177e4SLinus Torvalds .suffix = "ADC2",
15111da177e4SLinus Torvalds .capture_ops = &snd_intel8x0_capture2_ops,
15121da177e4SLinus Torvalds .prealloc_size = 0,
15131da177e4SLinus Torvalds .prealloc_max_size = 128 * 1024,
15141da177e4SLinus Torvalds .ac97_idx = ICHD_PCM2IN,
15151da177e4SLinus Torvalds },
15161da177e4SLinus Torvalds {
15171da177e4SLinus Torvalds .suffix = "IEC958",
15181da177e4SLinus Torvalds .playback_ops = &snd_intel8x0_spdif_ops,
15191da177e4SLinus Torvalds .prealloc_size = 64 * 1024,
15201da177e4SLinus Torvalds .prealloc_max_size = 128 * 1024,
15211da177e4SLinus Torvalds .ac97_idx = ICHD_SPBAR,
15221da177e4SLinus Torvalds },
15231da177e4SLinus Torvalds };
15241da177e4SLinus Torvalds
1525f729f88aSTakashi Iwai static const struct ich_pcm_table nforce_pcms[] = {
15261da177e4SLinus Torvalds {
15271da177e4SLinus Torvalds .playback_ops = &snd_intel8x0_playback_ops,
15281da177e4SLinus Torvalds .capture_ops = &snd_intel8x0_capture_ops,
15291da177e4SLinus Torvalds .prealloc_size = 64 * 1024,
15301da177e4SLinus Torvalds .prealloc_max_size = 128 * 1024,
15311da177e4SLinus Torvalds },
15321da177e4SLinus Torvalds {
15331da177e4SLinus Torvalds .suffix = "MIC ADC",
15341da177e4SLinus Torvalds .capture_ops = &snd_intel8x0_capture_mic_ops,
15351da177e4SLinus Torvalds .prealloc_size = 0,
15361da177e4SLinus Torvalds .prealloc_max_size = 128 * 1024,
15371da177e4SLinus Torvalds .ac97_idx = NVD_MIC,
15381da177e4SLinus Torvalds },
15391da177e4SLinus Torvalds {
15401da177e4SLinus Torvalds .suffix = "IEC958",
15411da177e4SLinus Torvalds .playback_ops = &snd_intel8x0_spdif_ops,
15421da177e4SLinus Torvalds .prealloc_size = 64 * 1024,
15431da177e4SLinus Torvalds .prealloc_max_size = 128 * 1024,
15441da177e4SLinus Torvalds .ac97_idx = NVD_SPBAR,
15451da177e4SLinus Torvalds },
15461da177e4SLinus Torvalds };
15471da177e4SLinus Torvalds
1548f729f88aSTakashi Iwai static const struct ich_pcm_table ali_pcms[] = {
15491da177e4SLinus Torvalds {
15501da177e4SLinus Torvalds .playback_ops = &snd_intel8x0_ali_playback_ops,
15511da177e4SLinus Torvalds .capture_ops = &snd_intel8x0_ali_capture_ops,
15521da177e4SLinus Torvalds .prealloc_size = 64 * 1024,
15531da177e4SLinus Torvalds .prealloc_max_size = 128 * 1024,
15541da177e4SLinus Torvalds },
15551da177e4SLinus Torvalds {
15561da177e4SLinus Torvalds .suffix = "MIC ADC",
15571da177e4SLinus Torvalds .capture_ops = &snd_intel8x0_ali_capture_mic_ops,
15581da177e4SLinus Torvalds .prealloc_size = 0,
15591da177e4SLinus Torvalds .prealloc_max_size = 128 * 1024,
15601da177e4SLinus Torvalds .ac97_idx = ALID_MIC,
15611da177e4SLinus Torvalds },
15621da177e4SLinus Torvalds {
15631da177e4SLinus Torvalds .suffix = "IEC958",
15641da177e4SLinus Torvalds .playback_ops = &snd_intel8x0_ali_ac97spdifout_ops,
15651a183131STakashi Iwai /* .capture_ops = &snd_intel8x0_ali_spdifin_ops, */
15661da177e4SLinus Torvalds .prealloc_size = 64 * 1024,
15671da177e4SLinus Torvalds .prealloc_max_size = 128 * 1024,
15681da177e4SLinus Torvalds .ac97_idx = ALID_AC97SPDIFOUT,
15691da177e4SLinus Torvalds },
15701da177e4SLinus Torvalds #if 0 // NYI
15711da177e4SLinus Torvalds {
15721da177e4SLinus Torvalds .suffix = "HW IEC958",
15731da177e4SLinus Torvalds .playback_ops = &snd_intel8x0_ali_spdifout_ops,
15741da177e4SLinus Torvalds .prealloc_size = 64 * 1024,
15751da177e4SLinus Torvalds .prealloc_max_size = 128 * 1024,
15761da177e4SLinus Torvalds },
15771da177e4SLinus Torvalds #endif
15781da177e4SLinus Torvalds };
15791da177e4SLinus Torvalds
snd_intel8x0_pcm(struct intel8x0 * chip)1580e23e7a14SBill Pemberton static int snd_intel8x0_pcm(struct intel8x0 *chip)
15811da177e4SLinus Torvalds {
15821da177e4SLinus Torvalds int i, tblsize, device, err;
1583f729f88aSTakashi Iwai const struct ich_pcm_table *tbl, *rec;
15841da177e4SLinus Torvalds
15851da177e4SLinus Torvalds switch (chip->device_type) {
15861da177e4SLinus Torvalds case DEVICE_INTEL_ICH4:
15871da177e4SLinus Torvalds tbl = intel_pcms;
15881da177e4SLinus Torvalds tblsize = ARRAY_SIZE(intel_pcms);
1589a9e99660STakashi Iwai if (spdif_aclink)
1590a9e99660STakashi Iwai tblsize--;
15911da177e4SLinus Torvalds break;
15921da177e4SLinus Torvalds case DEVICE_NFORCE:
15931da177e4SLinus Torvalds tbl = nforce_pcms;
15941da177e4SLinus Torvalds tblsize = ARRAY_SIZE(nforce_pcms);
1595a9e99660STakashi Iwai if (spdif_aclink)
1596a9e99660STakashi Iwai tblsize--;
15971da177e4SLinus Torvalds break;
15981da177e4SLinus Torvalds case DEVICE_ALI:
15991da177e4SLinus Torvalds tbl = ali_pcms;
16001da177e4SLinus Torvalds tblsize = ARRAY_SIZE(ali_pcms);
16011da177e4SLinus Torvalds break;
16021da177e4SLinus Torvalds default:
16031da177e4SLinus Torvalds tbl = intel_pcms;
16041da177e4SLinus Torvalds tblsize = 2;
16051da177e4SLinus Torvalds break;
16061da177e4SLinus Torvalds }
16071da177e4SLinus Torvalds
16081da177e4SLinus Torvalds device = 0;
16091da177e4SLinus Torvalds for (i = 0; i < tblsize; i++) {
16101da177e4SLinus Torvalds rec = tbl + i;
16111da177e4SLinus Torvalds if (i > 0 && rec->ac97_idx) {
16121da177e4SLinus Torvalds /* activate PCM only when associated AC'97 codec */
16131da177e4SLinus Torvalds if (! chip->ichd[rec->ac97_idx].pcm)
16141da177e4SLinus Torvalds continue;
16151da177e4SLinus Torvalds }
16161da177e4SLinus Torvalds err = snd_intel8x0_pcm1(chip, device, rec);
16171da177e4SLinus Torvalds if (err < 0)
16181da177e4SLinus Torvalds return err;
16191da177e4SLinus Torvalds device++;
16201da177e4SLinus Torvalds }
16211da177e4SLinus Torvalds
16221da177e4SLinus Torvalds chip->pcm_devs = device;
16231da177e4SLinus Torvalds return 0;
16241da177e4SLinus Torvalds }
16251da177e4SLinus Torvalds
16261da177e4SLinus Torvalds
16271da177e4SLinus Torvalds /*
16281da177e4SLinus Torvalds * Mixer part
16291da177e4SLinus Torvalds */
16301da177e4SLinus Torvalds
snd_intel8x0_mixer_free_ac97_bus(struct snd_ac97_bus * bus)16316b75a9d8STakashi Iwai static void snd_intel8x0_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
16321da177e4SLinus Torvalds {
16336b75a9d8STakashi Iwai struct intel8x0 *chip = bus->private_data;
16341da177e4SLinus Torvalds chip->ac97_bus = NULL;
16351da177e4SLinus Torvalds }
16361da177e4SLinus Torvalds
snd_intel8x0_mixer_free_ac97(struct snd_ac97 * ac97)16376b75a9d8STakashi Iwai static void snd_intel8x0_mixer_free_ac97(struct snd_ac97 *ac97)
16381da177e4SLinus Torvalds {
16396b75a9d8STakashi Iwai struct intel8x0 *chip = ac97->private_data;
16401da177e4SLinus Torvalds chip->ac97[ac97->num] = NULL;
16411da177e4SLinus Torvalds }
16421da177e4SLinus Torvalds
1643668d0616SArvind Yadav static const struct ac97_pcm ac97_pcm_defs[] = {
16441da177e4SLinus Torvalds /* front PCM */
16451da177e4SLinus Torvalds {
16461da177e4SLinus Torvalds .exclusive = 1,
16471da177e4SLinus Torvalds .r = { {
16481da177e4SLinus Torvalds .slots = (1 << AC97_SLOT_PCM_LEFT) |
16491da177e4SLinus Torvalds (1 << AC97_SLOT_PCM_RIGHT) |
16501da177e4SLinus Torvalds (1 << AC97_SLOT_PCM_CENTER) |
16511da177e4SLinus Torvalds (1 << AC97_SLOT_PCM_SLEFT) |
16521da177e4SLinus Torvalds (1 << AC97_SLOT_PCM_SRIGHT) |
16531da177e4SLinus Torvalds (1 << AC97_SLOT_LFE)
16541da177e4SLinus Torvalds },
16551da177e4SLinus Torvalds {
16561da177e4SLinus Torvalds .slots = (1 << AC97_SLOT_PCM_LEFT) |
16571da177e4SLinus Torvalds (1 << AC97_SLOT_PCM_RIGHT) |
16581da177e4SLinus Torvalds (1 << AC97_SLOT_PCM_LEFT_0) |
16591da177e4SLinus Torvalds (1 << AC97_SLOT_PCM_RIGHT_0)
16601da177e4SLinus Torvalds }
16611da177e4SLinus Torvalds }
16621da177e4SLinus Torvalds },
16631da177e4SLinus Torvalds /* PCM IN #1 */
16641da177e4SLinus Torvalds {
16651da177e4SLinus Torvalds .stream = 1,
16661da177e4SLinus Torvalds .exclusive = 1,
16671da177e4SLinus Torvalds .r = { {
16681da177e4SLinus Torvalds .slots = (1 << AC97_SLOT_PCM_LEFT) |
16691da177e4SLinus Torvalds (1 << AC97_SLOT_PCM_RIGHT)
16701da177e4SLinus Torvalds }
16711da177e4SLinus Torvalds }
16721da177e4SLinus Torvalds },
16731da177e4SLinus Torvalds /* MIC IN #1 */
16741da177e4SLinus Torvalds {
16751da177e4SLinus Torvalds .stream = 1,
16761da177e4SLinus Torvalds .exclusive = 1,
16771da177e4SLinus Torvalds .r = { {
16781da177e4SLinus Torvalds .slots = (1 << AC97_SLOT_MIC)
16791da177e4SLinus Torvalds }
16801da177e4SLinus Torvalds }
16811da177e4SLinus Torvalds },
16821da177e4SLinus Torvalds /* S/PDIF PCM */
16831da177e4SLinus Torvalds {
16841da177e4SLinus Torvalds .exclusive = 1,
16851da177e4SLinus Torvalds .spdif = 1,
16861da177e4SLinus Torvalds .r = { {
16871da177e4SLinus Torvalds .slots = (1 << AC97_SLOT_SPDIF_LEFT2) |
16881da177e4SLinus Torvalds (1 << AC97_SLOT_SPDIF_RIGHT2)
16891da177e4SLinus Torvalds }
16901da177e4SLinus Torvalds }
16911da177e4SLinus Torvalds },
16921da177e4SLinus Torvalds /* PCM IN #2 */
16931da177e4SLinus Torvalds {
16941da177e4SLinus Torvalds .stream = 1,
16951da177e4SLinus Torvalds .exclusive = 1,
16961da177e4SLinus Torvalds .r = { {
16971da177e4SLinus Torvalds .slots = (1 << AC97_SLOT_PCM_LEFT) |
16981da177e4SLinus Torvalds (1 << AC97_SLOT_PCM_RIGHT)
16991da177e4SLinus Torvalds }
17001da177e4SLinus Torvalds }
17011da177e4SLinus Torvalds },
17021da177e4SLinus Torvalds /* MIC IN #2 */
17031da177e4SLinus Torvalds {
17041da177e4SLinus Torvalds .stream = 1,
17051da177e4SLinus Torvalds .exclusive = 1,
17061da177e4SLinus Torvalds .r = { {
17071da177e4SLinus Torvalds .slots = (1 << AC97_SLOT_MIC)
17081da177e4SLinus Torvalds }
17091da177e4SLinus Torvalds }
17101da177e4SLinus Torvalds },
17111da177e4SLinus Torvalds };
17121da177e4SLinus Torvalds
1713eab0fbfaSJoe Perches static const struct ac97_quirk ac97_quirks[] = {
17140d9ac27aSTakashi Iwai {
17150d9ac27aSTakashi Iwai .subvendor = 0x0e11,
17160d9ac27aSTakashi Iwai .subdevice = 0x000e,
17170d9ac27aSTakashi Iwai .name = "Compaq Deskpro EN", /* AD1885 */
17180d9ac27aSTakashi Iwai .type = AC97_TUNE_HP_ONLY
17190d9ac27aSTakashi Iwai },
17201da177e4SLinus Torvalds {
17216fd8b87fSJames Courtier-Dutton .subvendor = 0x0e11,
17226fd8b87fSJames Courtier-Dutton .subdevice = 0x008a,
17231da177e4SLinus Torvalds .name = "Compaq Evo W4000", /* AD1885 */
17241da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
17251da177e4SLinus Torvalds },
17261da177e4SLinus Torvalds {
17276fd8b87fSJames Courtier-Dutton .subvendor = 0x0e11,
17286fd8b87fSJames Courtier-Dutton .subdevice = 0x00b8,
17291da177e4SLinus Torvalds .name = "Compaq Evo D510C",
17301da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
17311da177e4SLinus Torvalds },
17321da177e4SLinus Torvalds {
17336fd8b87fSJames Courtier-Dutton .subvendor = 0x0e11,
17346fd8b87fSJames Courtier-Dutton .subdevice = 0x0860,
17351da177e4SLinus Torvalds .name = "HP/Compaq nx7010",
17361da177e4SLinus Torvalds .type = AC97_TUNE_MUTE_LED
17371da177e4SLinus Torvalds },
17381da177e4SLinus Torvalds {
17396fd8b87fSJames Courtier-Dutton .subvendor = 0x1014,
17409c77b846SDaniel T Chen .subdevice = 0x0534,
17419c77b846SDaniel T Chen .name = "ThinkPad X31",
17429c77b846SDaniel T Chen .type = AC97_TUNE_INV_EAPD
17439c77b846SDaniel T Chen },
17449c77b846SDaniel T Chen {
17459c77b846SDaniel T Chen .subvendor = 0x1014,
17466fd8b87fSJames Courtier-Dutton .subdevice = 0x1f00,
17471da177e4SLinus Torvalds .name = "MS-9128",
17481da177e4SLinus Torvalds .type = AC97_TUNE_ALC_JACK
17491da177e4SLinus Torvalds },
17501da177e4SLinus Torvalds {
17515d529390STakashi Iwai .subvendor = 0x1014,
17525d529390STakashi Iwai .subdevice = 0x0267,
17535d529390STakashi Iwai .name = "IBM NetVista A30p", /* AD1981B */
17545d529390STakashi Iwai .type = AC97_TUNE_HP_ONLY
17555d529390STakashi Iwai },
17565d529390STakashi Iwai {
175772c8986cSDick Streefland .subvendor = 0x1025,
1758b6a370b6STakashi Iwai .subdevice = 0x0082,
1759b6a370b6STakashi Iwai .name = "Acer Travelmate 2310",
1760b6a370b6STakashi Iwai .type = AC97_TUNE_HP_ONLY
1761b6a370b6STakashi Iwai },
1762b6a370b6STakashi Iwai {
1763b6a370b6STakashi Iwai .subvendor = 0x1025,
176472c8986cSDick Streefland .subdevice = 0x0083,
176572c8986cSDick Streefland .name = "Acer Aspire 3003LCi",
176672c8986cSDick Streefland .type = AC97_TUNE_HP_ONLY
176772c8986cSDick Streefland },
176872c8986cSDick Streefland {
17696fd8b87fSJames Courtier-Dutton .subvendor = 0x1028,
17706fd8b87fSJames Courtier-Dutton .subdevice = 0x00d8,
17711da177e4SLinus Torvalds .name = "Dell Precision 530", /* AD1885 */
17721da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
17731da177e4SLinus Torvalds },
17741da177e4SLinus Torvalds {
17756fd8b87fSJames Courtier-Dutton .subvendor = 0x1028,
17766fd8b87fSJames Courtier-Dutton .subdevice = 0x010d,
17771da177e4SLinus Torvalds .name = "Dell", /* which model? AD1885 */
17781da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
17791da177e4SLinus Torvalds },
17801da177e4SLinus Torvalds {
17816fd8b87fSJames Courtier-Dutton .subvendor = 0x1028,
17826fd8b87fSJames Courtier-Dutton .subdevice = 0x0126,
17831da177e4SLinus Torvalds .name = "Dell Optiplex GX260", /* AD1981A */
17841da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
17851da177e4SLinus Torvalds },
17861da177e4SLinus Torvalds {
17876fd8b87fSJames Courtier-Dutton .subvendor = 0x1028,
17886fd8b87fSJames Courtier-Dutton .subdevice = 0x012c,
17891da177e4SLinus Torvalds .name = "Dell Precision 650", /* AD1981A */
17901da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
17911da177e4SLinus Torvalds },
17921da177e4SLinus Torvalds {
17936fd8b87fSJames Courtier-Dutton .subvendor = 0x1028,
17946fd8b87fSJames Courtier-Dutton .subdevice = 0x012d,
17951da177e4SLinus Torvalds .name = "Dell Precision 450", /* AD1981B*/
17961da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
17971da177e4SLinus Torvalds },
17981da177e4SLinus Torvalds {
17996fd8b87fSJames Courtier-Dutton .subvendor = 0x1028,
18006fd8b87fSJames Courtier-Dutton .subdevice = 0x0147,
18011da177e4SLinus Torvalds .name = "Dell", /* which model? AD1981B*/
18021da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
18031da177e4SLinus Torvalds },
18041da177e4SLinus Torvalds {
18056fd8b87fSJames Courtier-Dutton .subvendor = 0x1028,
1806c9fe51c4SChris Ball .subdevice = 0x0151,
1807c9fe51c4SChris Ball .name = "Dell Optiplex GX270", /* AD1981B */
1808c9fe51c4SChris Ball .type = AC97_TUNE_HP_ONLY
1809c9fe51c4SChris Ball },
1810c9fe51c4SChris Ball {
1811c9fe51c4SChris Ball .subvendor = 0x1028,
18121781a9afSDaniel T Chen .subdevice = 0x014e,
18131781a9afSDaniel T Chen .name = "Dell D800", /* STAC9750/51 */
18141781a9afSDaniel T Chen .type = AC97_TUNE_HP_ONLY
18151781a9afSDaniel T Chen },
18161781a9afSDaniel T Chen {
18171781a9afSDaniel T Chen .subvendor = 0x1028,
18186fd8b87fSJames Courtier-Dutton .subdevice = 0x0163,
18191da177e4SLinus Torvalds .name = "Dell Unknown", /* STAC9750/51 */
18201da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
18211da177e4SLinus Torvalds },
18221da177e4SLinus Torvalds {
18236d6f9156SKaroly Lorentey .subvendor = 0x1028,
1824c8283425SDaniel T Chen .subdevice = 0x016a,
1825c8283425SDaniel T Chen .name = "Dell Inspiron 8600", /* STAC9750/51 */
1826c8283425SDaniel T Chen .type = AC97_TUNE_HP_ONLY
1827c8283425SDaniel T Chen },
1828c8283425SDaniel T Chen {
1829c8283425SDaniel T Chen .subvendor = 0x1028,
18300613a594SDaniel T Chen .subdevice = 0x0182,
18310613a594SDaniel T Chen .name = "Dell Latitude D610", /* STAC9750/51 */
18320613a594SDaniel T Chen .type = AC97_TUNE_HP_ONLY
18330613a594SDaniel T Chen },
18340613a594SDaniel T Chen {
18350613a594SDaniel T Chen .subvendor = 0x1028,
18368286c53eSDaniel T Chen .subdevice = 0x0186,
18378286c53eSDaniel T Chen .name = "Dell Latitude D810", /* cf. Malone #41015 */
18388286c53eSDaniel T Chen .type = AC97_TUNE_HP_MUTE_LED
18398286c53eSDaniel T Chen },
18408286c53eSDaniel T Chen {
18418286c53eSDaniel T Chen .subvendor = 0x1028,
18428286c53eSDaniel T Chen .subdevice = 0x0188,
18438286c53eSDaniel T Chen .name = "Dell Inspiron 6000",
18448286c53eSDaniel T Chen .type = AC97_TUNE_HP_MUTE_LED /* cf. Malone #41015 */
18458286c53eSDaniel T Chen },
18468286c53eSDaniel T Chen {
18478286c53eSDaniel T Chen .subvendor = 0x1028,
1848f21169aaSDaniel T Chen .subdevice = 0x0189,
1849f21169aaSDaniel T Chen .name = "Dell Inspiron 9300",
1850f21169aaSDaniel T Chen .type = AC97_TUNE_HP_MUTE_LED
1851f21169aaSDaniel T Chen },
1852f21169aaSDaniel T Chen {
1853f21169aaSDaniel T Chen .subvendor = 0x1028,
18546d6f9156SKaroly Lorentey .subdevice = 0x0191,
18556d6f9156SKaroly Lorentey .name = "Dell Inspiron 8600",
18566d6f9156SKaroly Lorentey .type = AC97_TUNE_HP_ONLY
18576d6f9156SKaroly Lorentey },
18586d6f9156SKaroly Lorentey {
18596fd8b87fSJames Courtier-Dutton .subvendor = 0x103c,
18606fd8b87fSJames Courtier-Dutton .subdevice = 0x006d,
18611da177e4SLinus Torvalds .name = "HP zv5000",
18621da177e4SLinus Torvalds .type = AC97_TUNE_MUTE_LED /*AD1981B*/
18631da177e4SLinus Torvalds },
18641da177e4SLinus Torvalds { /* FIXME: which codec? */
18656fd8b87fSJames Courtier-Dutton .subvendor = 0x103c,
18666fd8b87fSJames Courtier-Dutton .subdevice = 0x00c3,
18671da177e4SLinus Torvalds .name = "HP xw6000",
18681da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
18691da177e4SLinus Torvalds },
18701da177e4SLinus Torvalds {
18716fd8b87fSJames Courtier-Dutton .subvendor = 0x103c,
18726fd8b87fSJames Courtier-Dutton .subdevice = 0x088c,
18731da177e4SLinus Torvalds .name = "HP nc8000",
18748286c53eSDaniel T Chen .type = AC97_TUNE_HP_MUTE_LED
18751da177e4SLinus Torvalds },
18761da177e4SLinus Torvalds {
18776fd8b87fSJames Courtier-Dutton .subvendor = 0x103c,
18786fd8b87fSJames Courtier-Dutton .subdevice = 0x0890,
18791da177e4SLinus Torvalds .name = "HP nc6000",
18801da177e4SLinus Torvalds .type = AC97_TUNE_MUTE_LED
18811da177e4SLinus Torvalds },
18821da177e4SLinus Torvalds {
18836fd8b87fSJames Courtier-Dutton .subvendor = 0x103c,
18846fd8b87fSJames Courtier-Dutton .subdevice = 0x129d,
18851da177e4SLinus Torvalds .name = "HP xw8000",
18861da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
18871da177e4SLinus Torvalds },
18881da177e4SLinus Torvalds {
18896fd8b87fSJames Courtier-Dutton .subvendor = 0x103c,
1890a0faefedSMatthew Garrett .subdevice = 0x0938,
1891a0faefedSMatthew Garrett .name = "HP nc4200",
1892a0faefedSMatthew Garrett .type = AC97_TUNE_HP_MUTE_LED
1893a0faefedSMatthew Garrett },
1894a0faefedSMatthew Garrett {
1895a0faefedSMatthew Garrett .subvendor = 0x103c,
1896a0faefedSMatthew Garrett .subdevice = 0x099c,
1897d82ed2ffSUlrich Mueller .name = "HP nx6110/nc6120",
1898a0faefedSMatthew Garrett .type = AC97_TUNE_HP_MUTE_LED
1899a0faefedSMatthew Garrett },
1900a0faefedSMatthew Garrett {
1901a0faefedSMatthew Garrett .subvendor = 0x103c,
1902a0faefedSMatthew Garrett .subdevice = 0x0944,
1903a0faefedSMatthew Garrett .name = "HP nc6220",
1904a0faefedSMatthew Garrett .type = AC97_TUNE_HP_MUTE_LED
1905a0faefedSMatthew Garrett },
1906a0faefedSMatthew Garrett {
1907a0faefedSMatthew Garrett .subvendor = 0x103c,
1908a0faefedSMatthew Garrett .subdevice = 0x0934,
1909a0faefedSMatthew Garrett .name = "HP nc8220",
1910a0faefedSMatthew Garrett .type = AC97_TUNE_HP_MUTE_LED
1911a0faefedSMatthew Garrett },
1912a0faefedSMatthew Garrett {
1913a0faefedSMatthew Garrett .subvendor = 0x103c,
19146fd8b87fSJames Courtier-Dutton .subdevice = 0x12f1,
19151da177e4SLinus Torvalds .name = "HP xw8200", /* AD1981B*/
19161da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
19171da177e4SLinus Torvalds },
19181da177e4SLinus Torvalds {
19196fd8b87fSJames Courtier-Dutton .subvendor = 0x103c,
19206fd8b87fSJames Courtier-Dutton .subdevice = 0x12f2,
19211da177e4SLinus Torvalds .name = "HP xw6200",
19221da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
19231da177e4SLinus Torvalds },
19241da177e4SLinus Torvalds {
19256fd8b87fSJames Courtier-Dutton .subvendor = 0x103c,
19266fd8b87fSJames Courtier-Dutton .subdevice = 0x3008,
19271da177e4SLinus Torvalds .name = "HP xw4200", /* AD1981B*/
19281da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
19291da177e4SLinus Torvalds },
19301da177e4SLinus Torvalds {
19316fd8b87fSJames Courtier-Dutton .subvendor = 0x104d,
19327e6c3989SDaniel T Chen .subdevice = 0x8144,
19337e6c3989SDaniel T Chen .name = "Sony",
19347e6c3989SDaniel T Chen .type = AC97_TUNE_INV_EAPD
19357e6c3989SDaniel T Chen },
19367e6c3989SDaniel T Chen {
19377e6c3989SDaniel T Chen .subvendor = 0x104d,
19386fd8b87fSJames Courtier-Dutton .subdevice = 0x8197,
19391da177e4SLinus Torvalds .name = "Sony S1XP",
19401da177e4SLinus Torvalds .type = AC97_TUNE_INV_EAPD
19411da177e4SLinus Torvalds },
19425da5b6f9SDaniel T Chen {
19435da5b6f9SDaniel T Chen .subvendor = 0x104d,
19445da5b6f9SDaniel T Chen .subdevice = 0x81c0,
19455da5b6f9SDaniel T Chen .name = "Sony VAIO VGN-T350P", /*AD1981B*/
19465da5b6f9SDaniel T Chen .type = AC97_TUNE_INV_EAPD
19475da5b6f9SDaniel T Chen },
1948ebb6f6acSDaniel T Chen {
1949ebb6f6acSDaniel T Chen .subvendor = 0x104d,
1950ebb6f6acSDaniel T Chen .subdevice = 0x81c5,
1951ebb6f6acSDaniel T Chen .name = "Sony VAIO VGN-B1VP", /*AD1981B*/
1952ebb6f6acSDaniel T Chen .type = AC97_TUNE_INV_EAPD
1953ebb6f6acSDaniel T Chen },
19541da177e4SLinus Torvalds {
19556fd8b87fSJames Courtier-Dutton .subvendor = 0x1043,
19566fd8b87fSJames Courtier-Dutton .subdevice = 0x80f3,
19571da177e4SLinus Torvalds .name = "ASUS ICH5/AD1985",
19581da177e4SLinus Torvalds .type = AC97_TUNE_AD_SHARING
19591da177e4SLinus Torvalds },
19601da177e4SLinus Torvalds {
19616fd8b87fSJames Courtier-Dutton .subvendor = 0x10cf,
19626fd8b87fSJames Courtier-Dutton .subdevice = 0x11c3,
19631da177e4SLinus Torvalds .name = "Fujitsu-Siemens E4010",
19641da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
19651da177e4SLinus Torvalds },
19661da177e4SLinus Torvalds {
19676fd8b87fSJames Courtier-Dutton .subvendor = 0x10cf,
196898c7f212STakashi Iwai .subdevice = 0x1225,
196998c7f212STakashi Iwai .name = "Fujitsu-Siemens T3010",
197098c7f212STakashi Iwai .type = AC97_TUNE_HP_ONLY
197198c7f212STakashi Iwai },
197298c7f212STakashi Iwai {
197398c7f212STakashi Iwai .subvendor = 0x10cf,
19746fd8b87fSJames Courtier-Dutton .subdevice = 0x1253,
19751da177e4SLinus Torvalds .name = "Fujitsu S6210", /* STAC9750/51 */
19761da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
19771da177e4SLinus Torvalds },
19781da177e4SLinus Torvalds {
19799970dce5STakashi Iwai .subvendor = 0x10cf,
198037c34ffbSTakashi Iwai .subdevice = 0x127d,
198137c34ffbSTakashi Iwai .name = "Fujitsu Lifebook P7010",
198237c34ffbSTakashi Iwai .type = AC97_TUNE_HP_ONLY
198337c34ffbSTakashi Iwai },
198437c34ffbSTakashi Iwai {
198537c34ffbSTakashi Iwai .subvendor = 0x10cf,
19868286c53eSDaniel T Chen .subdevice = 0x127e,
19878286c53eSDaniel T Chen .name = "Fujitsu Lifebook C1211D",
19888286c53eSDaniel T Chen .type = AC97_TUNE_HP_ONLY
19898286c53eSDaniel T Chen },
19908286c53eSDaniel T Chen {
19918286c53eSDaniel T Chen .subvendor = 0x10cf,
19929970dce5STakashi Iwai .subdevice = 0x12ec,
19939970dce5STakashi Iwai .name = "Fujitsu-Siemens 4010",
19949970dce5STakashi Iwai .type = AC97_TUNE_HP_ONLY
19959970dce5STakashi Iwai },
19969970dce5STakashi Iwai {
19972eb061f4SJaroslav Kysela .subvendor = 0x10cf,
19982eb061f4SJaroslav Kysela .subdevice = 0x12f2,
19992eb061f4SJaroslav Kysela .name = "Fujitsu-Siemens Celsius H320",
20002eb061f4SJaroslav Kysela .type = AC97_TUNE_SWAP_HP
20012eb061f4SJaroslav Kysela },
20022eb061f4SJaroslav Kysela {
20036fd8b87fSJames Courtier-Dutton .subvendor = 0x10f1,
20046fd8b87fSJames Courtier-Dutton .subdevice = 0x2665,
20051da177e4SLinus Torvalds .name = "Fujitsu-Siemens Celsius", /* AD1981? */
20061da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
20071da177e4SLinus Torvalds },
20081da177e4SLinus Torvalds {
20096fd8b87fSJames Courtier-Dutton .subvendor = 0x10f1,
20106fd8b87fSJames Courtier-Dutton .subdevice = 0x2885,
20111da177e4SLinus Torvalds .name = "AMD64 Mobo", /* ALC650 */
20121da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
20131da177e4SLinus Torvalds },
20141da177e4SLinus Torvalds {
20154f42bcc1STakashi Iwai .subvendor = 0x10f1,
20164f42bcc1STakashi Iwai .subdevice = 0x2895,
20174f42bcc1STakashi Iwai .name = "Tyan Thunder K8WE",
20184f42bcc1STakashi Iwai .type = AC97_TUNE_HP_ONLY
20194f42bcc1STakashi Iwai },
20204f42bcc1STakashi Iwai {
20216c504447SKeith Packard .subvendor = 0x10f7,
20226c504447SKeith Packard .subdevice = 0x834c,
20236c504447SKeith Packard .name = "Panasonic CF-R4",
20246c504447SKeith Packard .type = AC97_TUNE_HP_ONLY,
20256c504447SKeith Packard },
20266c504447SKeith Packard {
20276fd8b87fSJames Courtier-Dutton .subvendor = 0x110a,
20286fd8b87fSJames Courtier-Dutton .subdevice = 0x0056,
20291da177e4SLinus Torvalds .name = "Fujitsu-Siemens Scenic", /* AD1981? */
20301da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
20311da177e4SLinus Torvalds },
20321da177e4SLinus Torvalds {
20336fd8b87fSJames Courtier-Dutton .subvendor = 0x11d4,
20346fd8b87fSJames Courtier-Dutton .subdevice = 0x5375,
20351da177e4SLinus Torvalds .name = "ADI AD1985 (discrete)",
20361da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
20371da177e4SLinus Torvalds },
20381da177e4SLinus Torvalds {
20396fd8b87fSJames Courtier-Dutton .subvendor = 0x1462,
20406fd8b87fSJames Courtier-Dutton .subdevice = 0x5470,
20411da177e4SLinus Torvalds .name = "MSI P4 ATX 645 Ultra",
20421da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
20431da177e4SLinus Torvalds },
20441da177e4SLinus Torvalds {
2045bbb3c644SDaniel T Chen .subvendor = 0x161f,
204627c3afe6SDaniel T Chen .subdevice = 0x202f,
204727c3afe6SDaniel T Chen .name = "Gateway M520",
204827c3afe6SDaniel T Chen .type = AC97_TUNE_INV_EAPD
204927c3afe6SDaniel T Chen },
205027c3afe6SDaniel T Chen {
205127c3afe6SDaniel T Chen .subvendor = 0x161f,
2052bbb3c644SDaniel T Chen .subdevice = 0x203a,
2053bbb3c644SDaniel T Chen .name = "Gateway 4525GZ", /* AD1981B */
2054bbb3c644SDaniel T Chen .type = AC97_TUNE_INV_EAPD
2055bbb3c644SDaniel T Chen },
2056bbb3c644SDaniel T Chen {
20576fd8b87fSJames Courtier-Dutton .subvendor = 0x1734,
20586fd8b87fSJames Courtier-Dutton .subdevice = 0x0088,
20591da177e4SLinus Torvalds .name = "Fujitsu-Siemens D1522", /* AD1981 */
20601da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
20611da177e4SLinus Torvalds },
20621da177e4SLinus Torvalds {
20636fd8b87fSJames Courtier-Dutton .subvendor = 0x8086,
20646fd8b87fSJames Courtier-Dutton .subdevice = 0x2000,
20651da177e4SLinus Torvalds .mask = 0xfff0,
20661da177e4SLinus Torvalds .name = "Intel ICH5/AD1985",
20671da177e4SLinus Torvalds .type = AC97_TUNE_AD_SHARING
20681da177e4SLinus Torvalds },
20691da177e4SLinus Torvalds {
20706fd8b87fSJames Courtier-Dutton .subvendor = 0x8086,
20716fd8b87fSJames Courtier-Dutton .subdevice = 0x4000,
20721da177e4SLinus Torvalds .mask = 0xfff0,
20731da177e4SLinus Torvalds .name = "Intel ICH5/AD1985",
20741da177e4SLinus Torvalds .type = AC97_TUNE_AD_SHARING
20751da177e4SLinus Torvalds },
20761da177e4SLinus Torvalds {
20776fd8b87fSJames Courtier-Dutton .subvendor = 0x8086,
20786fd8b87fSJames Courtier-Dutton .subdevice = 0x4856,
20791da177e4SLinus Torvalds .name = "Intel D845WN (82801BA)",
20801da177e4SLinus Torvalds .type = AC97_TUNE_SWAP_HP
20811da177e4SLinus Torvalds },
20821da177e4SLinus Torvalds {
20836fd8b87fSJames Courtier-Dutton .subvendor = 0x8086,
20846fd8b87fSJames Courtier-Dutton .subdevice = 0x4d44,
20851da177e4SLinus Torvalds .name = "Intel D850EMV2", /* AD1885 */
20861da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
20871da177e4SLinus Torvalds },
20881da177e4SLinus Torvalds {
20896fd8b87fSJames Courtier-Dutton .subvendor = 0x8086,
20906fd8b87fSJames Courtier-Dutton .subdevice = 0x4d56,
20911da177e4SLinus Torvalds .name = "Intel ICH/AD1885",
20921da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
20931da177e4SLinus Torvalds },
20941da177e4SLinus Torvalds {
20956fd8b87fSJames Courtier-Dutton .subvendor = 0x8086,
20966fd8b87fSJames Courtier-Dutton .subdevice = 0x6000,
20971da177e4SLinus Torvalds .mask = 0xfff0,
20981da177e4SLinus Torvalds .name = "Intel ICH5/AD1985",
20991da177e4SLinus Torvalds .type = AC97_TUNE_AD_SHARING
21001da177e4SLinus Torvalds },
21011da177e4SLinus Torvalds {
21026fd8b87fSJames Courtier-Dutton .subvendor = 0x8086,
21036fd8b87fSJames Courtier-Dutton .subdevice = 0xe000,
21041da177e4SLinus Torvalds .mask = 0xfff0,
21051da177e4SLinus Torvalds .name = "Intel ICH5/AD1985",
21061da177e4SLinus Torvalds .type = AC97_TUNE_AD_SHARING
21071da177e4SLinus Torvalds },
21081da177e4SLinus Torvalds #if 0 /* FIXME: this seems wrong on most boards */
21091da177e4SLinus Torvalds {
21106fd8b87fSJames Courtier-Dutton .subvendor = 0x8086,
21116fd8b87fSJames Courtier-Dutton .subdevice = 0xa000,
21121da177e4SLinus Torvalds .mask = 0xfff0,
21131da177e4SLinus Torvalds .name = "Intel ICH5/AD1985",
21141da177e4SLinus Torvalds .type = AC97_TUNE_HP_ONLY
21151da177e4SLinus Torvalds },
21161da177e4SLinus Torvalds #endif
21171da177e4SLinus Torvalds { } /* terminator */
21181da177e4SLinus Torvalds };
21191da177e4SLinus Torvalds
snd_intel8x0_mixer(struct intel8x0 * chip,int ac97_clock,const char * quirk_override)2120e23e7a14SBill Pemberton static int snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,
21216b75a9d8STakashi Iwai const char *quirk_override)
21221da177e4SLinus Torvalds {
21236b75a9d8STakashi Iwai struct snd_ac97_bus *pbus;
21246b75a9d8STakashi Iwai struct snd_ac97_template ac97;
21251da177e4SLinus Torvalds int err;
21261da177e4SLinus Torvalds unsigned int i, codecs;
21271da177e4SLinus Torvalds unsigned int glob_sta = 0;
212851055da5STakashi Iwai const struct snd_ac97_bus_ops *ops;
212951055da5STakashi Iwai static const struct snd_ac97_bus_ops standard_bus_ops = {
21301da177e4SLinus Torvalds .write = snd_intel8x0_codec_write,
21311da177e4SLinus Torvalds .read = snd_intel8x0_codec_read,
21321da177e4SLinus Torvalds };
213351055da5STakashi Iwai static const struct snd_ac97_bus_ops ali_bus_ops = {
21341da177e4SLinus Torvalds .write = snd_intel8x0_ali_codec_write,
21351da177e4SLinus Torvalds .read = snd_intel8x0_ali_codec_read,
21361da177e4SLinus Torvalds };
21371da177e4SLinus Torvalds
21381da177e4SLinus Torvalds chip->spdif_idx = -1; /* use PCMOUT (or disabled) */
2139a9e99660STakashi Iwai if (!spdif_aclink) {
21401da177e4SLinus Torvalds switch (chip->device_type) {
21411da177e4SLinus Torvalds case DEVICE_NFORCE:
21421da177e4SLinus Torvalds chip->spdif_idx = NVD_SPBAR;
21431da177e4SLinus Torvalds break;
21441da177e4SLinus Torvalds case DEVICE_ALI:
21451da177e4SLinus Torvalds chip->spdif_idx = ALID_AC97SPDIFOUT;
21461da177e4SLinus Torvalds break;
21471da177e4SLinus Torvalds case DEVICE_INTEL_ICH4:
21481da177e4SLinus Torvalds chip->spdif_idx = ICHD_SPBAR;
21491da177e4SLinus Torvalds break;
2150395d9dd5SPeter Senna Tschudin }
2151a9e99660STakashi Iwai }
21521da177e4SLinus Torvalds
21531da177e4SLinus Torvalds chip->in_ac97_init = 1;
21541da177e4SLinus Torvalds
21551da177e4SLinus Torvalds memset(&ac97, 0, sizeof(ac97));
21561da177e4SLinus Torvalds ac97.private_data = chip;
21571da177e4SLinus Torvalds ac97.private_free = snd_intel8x0_mixer_free_ac97;
2158f1a63a38STakashi Iwai ac97.scaps = AC97_SCAP_SKIP_MODEM | AC97_SCAP_POWER_SAVE;
21591da177e4SLinus Torvalds if (chip->xbox)
21601da177e4SLinus Torvalds ac97.scaps |= AC97_SCAP_DETECT_BY_VENDOR;
21611da177e4SLinus Torvalds if (chip->device_type != DEVICE_ALI) {
21621da177e4SLinus Torvalds glob_sta = igetdword(chip, ICHREG(GLOB_STA));
21631da177e4SLinus Torvalds ops = &standard_bus_ops;
21641da177e4SLinus Torvalds chip->in_sdin_init = 1;
216584a43bd5STakashi Iwai codecs = 0;
216684a43bd5STakashi Iwai for (i = 0; i < chip->max_codecs; i++) {
216784a43bd5STakashi Iwai if (! (glob_sta & chip->codec_bit[i]))
216884a43bd5STakashi Iwai continue;
216984a43bd5STakashi Iwai if (chip->device_type == DEVICE_INTEL_ICH4) {
217084a43bd5STakashi Iwai snd_intel8x0_codec_read_test(chip, codecs);
217184a43bd5STakashi Iwai chip->ac97_sdin[codecs] =
217284a43bd5STakashi Iwai igetbyte(chip, ICHREG(SDM)) & ICH_LDI_MASK;
2173da3cec35STakashi Iwai if (snd_BUG_ON(chip->ac97_sdin[codecs] >= 3))
2174da3cec35STakashi Iwai chip->ac97_sdin[codecs] = 0;
217584a43bd5STakashi Iwai } else
217684a43bd5STakashi Iwai chip->ac97_sdin[codecs] = i;
217784a43bd5STakashi Iwai codecs++;
21781da177e4SLinus Torvalds }
21791da177e4SLinus Torvalds chip->in_sdin_init = 0;
218084a43bd5STakashi Iwai if (! codecs)
218184a43bd5STakashi Iwai codecs = 1;
21821da177e4SLinus Torvalds } else {
21831da177e4SLinus Torvalds ops = &ali_bus_ops;
21841da177e4SLinus Torvalds codecs = 1;
21851da177e4SLinus Torvalds /* detect the secondary codec */
21861da177e4SLinus Torvalds for (i = 0; i < 100; i++) {
21871da177e4SLinus Torvalds unsigned int reg = igetdword(chip, ICHREG(ALI_RTSR));
21881da177e4SLinus Torvalds if (reg & 0x40) {
21891da177e4SLinus Torvalds codecs = 2;
21901da177e4SLinus Torvalds break;
21911da177e4SLinus Torvalds }
21921da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_RTSR), reg | 0x40);
21931da177e4SLinus Torvalds udelay(1);
21941da177e4SLinus Torvalds }
21951da177e4SLinus Torvalds }
21963a5f3dd3STakashi Iwai err = snd_ac97_bus(chip->card, 0, ops, chip, &pbus);
21973a5f3dd3STakashi Iwai if (err < 0)
21981da177e4SLinus Torvalds goto __err;
21991da177e4SLinus Torvalds pbus->private_free = snd_intel8x0_mixer_free_ac97_bus;
22001da177e4SLinus Torvalds if (ac97_clock >= 8000 && ac97_clock <= 48000)
22011da177e4SLinus Torvalds pbus->clock = ac97_clock;
22021da177e4SLinus Torvalds /* FIXME: my test board doesn't work well with VRA... */
22031da177e4SLinus Torvalds if (chip->device_type == DEVICE_ALI)
22041da177e4SLinus Torvalds pbus->no_vra = 1;
22051da177e4SLinus Torvalds else
22061da177e4SLinus Torvalds pbus->dra = 1;
22071da177e4SLinus Torvalds chip->ac97_bus = pbus;
220884a43bd5STakashi Iwai chip->ncodecs = codecs;
22091da177e4SLinus Torvalds
22101da177e4SLinus Torvalds ac97.pci = chip->pci;
22111da177e4SLinus Torvalds for (i = 0; i < codecs; i++) {
22121da177e4SLinus Torvalds ac97.num = i;
22133a5f3dd3STakashi Iwai err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i]);
22143a5f3dd3STakashi Iwai if (err < 0) {
22151da177e4SLinus Torvalds if (err != -EACCES)
2216f493e7bcSTakashi Iwai dev_err(chip->card->dev,
2217f493e7bcSTakashi Iwai "Unable to initialize codec #%d\n", i);
22181da177e4SLinus Torvalds if (i == 0)
22191da177e4SLinus Torvalds goto __err;
22201da177e4SLinus Torvalds }
22211da177e4SLinus Torvalds }
22221da177e4SLinus Torvalds /* tune up the primary codec */
22231da177e4SLinus Torvalds snd_ac97_tune_hardware(chip->ac97[0], ac97_quirks, quirk_override);
22241da177e4SLinus Torvalds /* enable separate SDINs for ICH4 */
22251da177e4SLinus Torvalds if (chip->device_type == DEVICE_INTEL_ICH4)
22261da177e4SLinus Torvalds pbus->isdin = 1;
22271da177e4SLinus Torvalds /* find the available PCM streams */
22281da177e4SLinus Torvalds i = ARRAY_SIZE(ac97_pcm_defs);
22291da177e4SLinus Torvalds if (chip->device_type != DEVICE_INTEL_ICH4)
22301da177e4SLinus Torvalds i -= 2; /* do not allocate PCM2IN and MIC2 */
22311da177e4SLinus Torvalds if (chip->spdif_idx < 0)
22321da177e4SLinus Torvalds i--; /* do not allocate S/PDIF */
22331da177e4SLinus Torvalds err = snd_ac97_pcm_assign(pbus, i, ac97_pcm_defs);
22341da177e4SLinus Torvalds if (err < 0)
22351da177e4SLinus Torvalds goto __err;
22361da177e4SLinus Torvalds chip->ichd[ICHD_PCMOUT].pcm = &pbus->pcms[0];
22371da177e4SLinus Torvalds chip->ichd[ICHD_PCMIN].pcm = &pbus->pcms[1];
22381da177e4SLinus Torvalds chip->ichd[ICHD_MIC].pcm = &pbus->pcms[2];
22391da177e4SLinus Torvalds if (chip->spdif_idx >= 0)
22401da177e4SLinus Torvalds chip->ichd[chip->spdif_idx].pcm = &pbus->pcms[3];
22411da177e4SLinus Torvalds if (chip->device_type == DEVICE_INTEL_ICH4) {
22421da177e4SLinus Torvalds chip->ichd[ICHD_PCM2IN].pcm = &pbus->pcms[4];
22431da177e4SLinus Torvalds chip->ichd[ICHD_MIC2].pcm = &pbus->pcms[5];
22441da177e4SLinus Torvalds }
22451da177e4SLinus Torvalds /* enable separate SDINs for ICH4 */
22461da177e4SLinus Torvalds if (chip->device_type == DEVICE_INTEL_ICH4) {
22471da177e4SLinus Torvalds struct ac97_pcm *pcm = chip->ichd[ICHD_PCM2IN].pcm;
22481da177e4SLinus Torvalds u8 tmp = igetbyte(chip, ICHREG(SDM));
22491da177e4SLinus Torvalds tmp &= ~(ICH_DI2L_MASK|ICH_DI1L_MASK);
22501da177e4SLinus Torvalds if (pcm) {
22511da177e4SLinus Torvalds tmp |= ICH_SE; /* steer enable for multiple SDINs */
22521da177e4SLinus Torvalds tmp |= chip->ac97_sdin[0] << ICH_DI1L_SHIFT;
22531da177e4SLinus Torvalds for (i = 1; i < 4; i++) {
22541da177e4SLinus Torvalds if (pcm->r[0].codec[i]) {
22551da177e4SLinus Torvalds tmp |= chip->ac97_sdin[pcm->r[0].codec[1]->num] << ICH_DI2L_SHIFT;
22561da177e4SLinus Torvalds break;
22571da177e4SLinus Torvalds }
22581da177e4SLinus Torvalds }
22591da177e4SLinus Torvalds } else {
22601da177e4SLinus Torvalds tmp &= ~ICH_SE; /* steer disable */
22611da177e4SLinus Torvalds }
22621da177e4SLinus Torvalds iputbyte(chip, ICHREG(SDM), tmp);
22631da177e4SLinus Torvalds }
22641da177e4SLinus Torvalds if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_PCM_SLEFT)) {
22651da177e4SLinus Torvalds chip->multi4 = 1;
22664235a317STakashi Iwai if (pbus->pcms[0].r[0].slots & (1 << AC97_SLOT_LFE)) {
22671da177e4SLinus Torvalds chip->multi6 = 1;
22684235a317STakashi Iwai if (chip->ac97[0]->flags & AC97_HAS_8CH)
22694235a317STakashi Iwai chip->multi8 = 1;
22704235a317STakashi Iwai }
22711da177e4SLinus Torvalds }
22721da177e4SLinus Torvalds if (pbus->pcms[0].r[1].rslots[0]) {
22731da177e4SLinus Torvalds chip->dra = 1;
22741da177e4SLinus Torvalds }
22751da177e4SLinus Torvalds if (chip->device_type == DEVICE_INTEL_ICH4) {
22761da177e4SLinus Torvalds if ((igetdword(chip, ICHREG(GLOB_STA)) & ICH_SAMPLE_CAP) == ICH_SAMPLE_16_20)
22771da177e4SLinus Torvalds chip->smp20bit = 1;
22781da177e4SLinus Torvalds }
2279a9e99660STakashi Iwai if (chip->device_type == DEVICE_NFORCE && !spdif_aclink) {
22801da177e4SLinus Torvalds /* 48kHz only */
22811da177e4SLinus Torvalds chip->ichd[chip->spdif_idx].pcm->rates = SNDRV_PCM_RATE_48000;
22821da177e4SLinus Torvalds }
2283a9e99660STakashi Iwai if (chip->device_type == DEVICE_INTEL_ICH4 && !spdif_aclink) {
22841da177e4SLinus Torvalds /* use slot 10/11 for SPDIF */
22851da177e4SLinus Torvalds u32 val;
22861da177e4SLinus Torvalds val = igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_PCM_SPDIF_MASK;
22871da177e4SLinus Torvalds val |= ICH_PCM_SPDIF_1011;
22881da177e4SLinus Torvalds iputdword(chip, ICHREG(GLOB_CNT), val);
22891da177e4SLinus Torvalds snd_ac97_update_bits(chip->ac97[0], AC97_EXTENDED_STATUS, 0x03 << 4, 0x03 << 4);
22901da177e4SLinus Torvalds }
22911da177e4SLinus Torvalds chip->in_ac97_init = 0;
22921da177e4SLinus Torvalds return 0;
22931da177e4SLinus Torvalds
22941da177e4SLinus Torvalds __err:
22951da177e4SLinus Torvalds /* clear the cold-reset bit for the next chance */
22961da177e4SLinus Torvalds if (chip->device_type != DEVICE_ALI)
22976b75a9d8STakashi Iwai iputdword(chip, ICHREG(GLOB_CNT),
22986b75a9d8STakashi Iwai igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_AC97COLD);
22991da177e4SLinus Torvalds return err;
23001da177e4SLinus Torvalds }
23011da177e4SLinus Torvalds
23021da177e4SLinus Torvalds
23031da177e4SLinus Torvalds /*
23041da177e4SLinus Torvalds *
23051da177e4SLinus Torvalds */
23061da177e4SLinus Torvalds
do_ali_reset(struct intel8x0 * chip)23076b75a9d8STakashi Iwai static void do_ali_reset(struct intel8x0 *chip)
23081da177e4SLinus Torvalds {
23091da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_SCR), ICH_ALI_SC_RESET);
23101da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_FIFOCR1), 0x83838383);
23111da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_FIFOCR2), 0x83838383);
23121da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_FIFOCR3), 0x83838383);
23131da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_INTERFACECR),
2314d78bec21SWei Ni ICH_ALI_IF_PI|ICH_ALI_IF_PO);
23151da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_INTERRUPTCR), 0x00000000);
23161da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_INTERRUPTSR), 0x00000000);
23171da177e4SLinus Torvalds }
23181da177e4SLinus Torvalds
2319e3e9c5e7SThadeu Lima de Souza Cascardo #ifdef CONFIG_SND_AC97_POWER_SAVE
232088e540a8STakashi Iwai static const struct snd_pci_quirk ich_chip_reset_mode[] = {
2321e3e9c5e7SThadeu Lima de Souza Cascardo SND_PCI_QUIRK(0x1014, 0x051f, "Thinkpad R32", 1),
2322e3e9c5e7SThadeu Lima de Souza Cascardo { } /* end */
2323e3e9c5e7SThadeu Lima de Souza Cascardo };
2324e3e9c5e7SThadeu Lima de Souza Cascardo
snd_intel8x0_ich_chip_cold_reset(struct intel8x0 * chip)2325e3e9c5e7SThadeu Lima de Souza Cascardo static int snd_intel8x0_ich_chip_cold_reset(struct intel8x0 *chip)
2326e3e9c5e7SThadeu Lima de Souza Cascardo {
2327e3e9c5e7SThadeu Lima de Souza Cascardo unsigned int cnt;
2328e3e9c5e7SThadeu Lima de Souza Cascardo /* ACLink on, 2 channels */
2329e3e9c5e7SThadeu Lima de Souza Cascardo
2330e3e9c5e7SThadeu Lima de Souza Cascardo if (snd_pci_quirk_lookup(chip->pci, ich_chip_reset_mode))
2331e3e9c5e7SThadeu Lima de Souza Cascardo return -EIO;
2332e3e9c5e7SThadeu Lima de Souza Cascardo
2333e3e9c5e7SThadeu Lima de Souza Cascardo cnt = igetdword(chip, ICHREG(GLOB_CNT));
2334e3e9c5e7SThadeu Lima de Souza Cascardo cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK);
2335e3e9c5e7SThadeu Lima de Souza Cascardo
2336e3e9c5e7SThadeu Lima de Souza Cascardo /* do cold reset - the full ac97 powerdown may leave the controller
2337e3e9c5e7SThadeu Lima de Souza Cascardo * in a warm state but actually it cannot communicate with the codec.
2338e3e9c5e7SThadeu Lima de Souza Cascardo */
2339e3e9c5e7SThadeu Lima de Souza Cascardo iputdword(chip, ICHREG(GLOB_CNT), cnt & ~ICH_AC97COLD);
2340e3e9c5e7SThadeu Lima de Souza Cascardo cnt = igetdword(chip, ICHREG(GLOB_CNT));
2341e3e9c5e7SThadeu Lima de Souza Cascardo udelay(10);
2342e3e9c5e7SThadeu Lima de Souza Cascardo iputdword(chip, ICHREG(GLOB_CNT), cnt | ICH_AC97COLD);
2343e3e9c5e7SThadeu Lima de Souza Cascardo msleep(1);
2344e3e9c5e7SThadeu Lima de Souza Cascardo return 0;
2345e3e9c5e7SThadeu Lima de Souza Cascardo }
2346e3e9c5e7SThadeu Lima de Souza Cascardo #define snd_intel8x0_ich_chip_can_cold_reset(chip) \
2347e3e9c5e7SThadeu Lima de Souza Cascardo (!snd_pci_quirk_lookup(chip->pci, ich_chip_reset_mode))
2348e3e9c5e7SThadeu Lima de Souza Cascardo #else
2349e1672800STakashi Iwai #define snd_intel8x0_ich_chip_cold_reset(chip) 0
2350e3e9c5e7SThadeu Lima de Souza Cascardo #define snd_intel8x0_ich_chip_can_cold_reset(chip) (0)
2351e3e9c5e7SThadeu Lima de Souza Cascardo #endif
2352e3e9c5e7SThadeu Lima de Souza Cascardo
snd_intel8x0_ich_chip_reset(struct intel8x0 * chip)2353e3e9c5e7SThadeu Lima de Souza Cascardo static int snd_intel8x0_ich_chip_reset(struct intel8x0 *chip)
2354e3e9c5e7SThadeu Lima de Souza Cascardo {
2355e3e9c5e7SThadeu Lima de Souza Cascardo unsigned long end_time;
2356e3e9c5e7SThadeu Lima de Souza Cascardo unsigned int cnt;
2357e3e9c5e7SThadeu Lima de Souza Cascardo /* ACLink on, 2 channels */
2358e3e9c5e7SThadeu Lima de Souza Cascardo cnt = igetdword(chip, ICHREG(GLOB_CNT));
2359e3e9c5e7SThadeu Lima de Souza Cascardo cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK);
2360e3e9c5e7SThadeu Lima de Souza Cascardo /* finish cold or do warm reset */
2361e3e9c5e7SThadeu Lima de Souza Cascardo cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
2362e3e9c5e7SThadeu Lima de Souza Cascardo iputdword(chip, ICHREG(GLOB_CNT), cnt);
2363e3e9c5e7SThadeu Lima de Souza Cascardo end_time = (jiffies + (HZ / 4)) + 1;
2364e3e9c5e7SThadeu Lima de Souza Cascardo do {
2365e3e9c5e7SThadeu Lima de Souza Cascardo if ((igetdword(chip, ICHREG(GLOB_CNT)) & ICH_AC97WARM) == 0)
2366e3e9c5e7SThadeu Lima de Souza Cascardo return 0;
2367e3e9c5e7SThadeu Lima de Souza Cascardo schedule_timeout_uninterruptible(1);
2368e3e9c5e7SThadeu Lima de Souza Cascardo } while (time_after_eq(end_time, jiffies));
2369f493e7bcSTakashi Iwai dev_err(chip->card->dev, "AC'97 warm reset still in progress? [0x%x]\n",
2370e3e9c5e7SThadeu Lima de Souza Cascardo igetdword(chip, ICHREG(GLOB_CNT)));
2371e3e9c5e7SThadeu Lima de Souza Cascardo return -EIO;
2372e3e9c5e7SThadeu Lima de Souza Cascardo }
2373e3e9c5e7SThadeu Lima de Souza Cascardo
snd_intel8x0_ich_chip_init(struct intel8x0 * chip,int probing)23746b75a9d8STakashi Iwai static int snd_intel8x0_ich_chip_init(struct intel8x0 *chip, int probing)
23751da177e4SLinus Torvalds {
23761da177e4SLinus Torvalds unsigned long end_time;
2377e3e9c5e7SThadeu Lima de Souza Cascardo unsigned int status, nstatus;
2378e3e9c5e7SThadeu Lima de Souza Cascardo unsigned int cnt;
2379e3e9c5e7SThadeu Lima de Souza Cascardo int err;
23801da177e4SLinus Torvalds
23811da177e4SLinus Torvalds /* put logic to right state */
23821da177e4SLinus Torvalds /* first clear status bits */
23831da177e4SLinus Torvalds status = ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT;
23841da177e4SLinus Torvalds if (chip->device_type == DEVICE_NFORCE)
23851da177e4SLinus Torvalds status |= ICH_NVSPINT;
23861da177e4SLinus Torvalds cnt = igetdword(chip, ICHREG(GLOB_STA));
23871da177e4SLinus Torvalds iputdword(chip, ICHREG(GLOB_STA), cnt & status);
23881da177e4SLinus Torvalds
2389e3e9c5e7SThadeu Lima de Souza Cascardo if (snd_intel8x0_ich_chip_can_cold_reset(chip))
2390e3e9c5e7SThadeu Lima de Souza Cascardo err = snd_intel8x0_ich_chip_cold_reset(chip);
2391e3e9c5e7SThadeu Lima de Souza Cascardo else
2392e3e9c5e7SThadeu Lima de Souza Cascardo err = snd_intel8x0_ich_chip_reset(chip);
2393e3e9c5e7SThadeu Lima de Souza Cascardo if (err < 0)
2394e3e9c5e7SThadeu Lima de Souza Cascardo return err;
23951da177e4SLinus Torvalds
23961da177e4SLinus Torvalds if (probing) {
23971da177e4SLinus Torvalds /* wait for any codec ready status.
23981da177e4SLinus Torvalds * Once it becomes ready it should remain ready
23991da177e4SLinus Torvalds * as long as we do not disable the ac97 link.
24001da177e4SLinus Torvalds */
24011da177e4SLinus Torvalds end_time = jiffies + HZ;
24021da177e4SLinus Torvalds do {
24036b75a9d8STakashi Iwai status = igetdword(chip, ICHREG(GLOB_STA)) &
240484a43bd5STakashi Iwai chip->codec_isr_bits;
24051da177e4SLinus Torvalds if (status)
24061da177e4SLinus Torvalds break;
2407954bea35STakashi Iwai schedule_timeout_uninterruptible(1);
24081da177e4SLinus Torvalds } while (time_after_eq(end_time, jiffies));
24091da177e4SLinus Torvalds if (! status) {
24101da177e4SLinus Torvalds /* no codec is found */
2411f493e7bcSTakashi Iwai dev_err(chip->card->dev,
2412f493e7bcSTakashi Iwai "codec_ready: codec is not ready [0x%x]\n",
24136b75a9d8STakashi Iwai igetdword(chip, ICHREG(GLOB_STA)));
24141da177e4SLinus Torvalds return -EIO;
24151da177e4SLinus Torvalds }
24161da177e4SLinus Torvalds
24171da177e4SLinus Torvalds /* wait for other codecs ready status. */
24181da177e4SLinus Torvalds end_time = jiffies + HZ / 4;
241984a43bd5STakashi Iwai while (status != chip->codec_isr_bits &&
242084a43bd5STakashi Iwai time_after_eq(end_time, jiffies)) {
2421954bea35STakashi Iwai schedule_timeout_uninterruptible(1);
242284a43bd5STakashi Iwai status |= igetdword(chip, ICHREG(GLOB_STA)) &
242384a43bd5STakashi Iwai chip->codec_isr_bits;
24241da177e4SLinus Torvalds }
24251da177e4SLinus Torvalds
24261da177e4SLinus Torvalds } else {
24271da177e4SLinus Torvalds /* resume phase */
24281da177e4SLinus Torvalds int i;
24291da177e4SLinus Torvalds status = 0;
243084a43bd5STakashi Iwai for (i = 0; i < chip->ncodecs; i++)
24311da177e4SLinus Torvalds if (chip->ac97[i])
243284a43bd5STakashi Iwai status |= chip->codec_bit[chip->ac97_sdin[i]];
24331da177e4SLinus Torvalds /* wait until all the probed codecs are ready */
24341da177e4SLinus Torvalds end_time = jiffies + HZ;
24351da177e4SLinus Torvalds do {
24366b75a9d8STakashi Iwai nstatus = igetdword(chip, ICHREG(GLOB_STA)) &
243784a43bd5STakashi Iwai chip->codec_isr_bits;
24381da177e4SLinus Torvalds if (status == nstatus)
24391da177e4SLinus Torvalds break;
2440954bea35STakashi Iwai schedule_timeout_uninterruptible(1);
24411da177e4SLinus Torvalds } while (time_after_eq(end_time, jiffies));
24421da177e4SLinus Torvalds }
24431da177e4SLinus Torvalds
24441da177e4SLinus Torvalds if (chip->device_type == DEVICE_SIS) {
24451da177e4SLinus Torvalds /* unmute the output on SIS7012 */
24461da177e4SLinus Torvalds iputword(chip, 0x4c, igetword(chip, 0x4c) | 1);
24471da177e4SLinus Torvalds }
2448a9e99660STakashi Iwai if (chip->device_type == DEVICE_NFORCE && !spdif_aclink) {
24491da177e4SLinus Torvalds /* enable SPDIF interrupt */
24501da177e4SLinus Torvalds unsigned int val;
24511da177e4SLinus Torvalds pci_read_config_dword(chip->pci, 0x4c, &val);
24521da177e4SLinus Torvalds val |= 0x1000000;
24531da177e4SLinus Torvalds pci_write_config_dword(chip->pci, 0x4c, val);
24541da177e4SLinus Torvalds }
24551da177e4SLinus Torvalds return 0;
24561da177e4SLinus Torvalds }
24571da177e4SLinus Torvalds
snd_intel8x0_ali_chip_init(struct intel8x0 * chip,int probing)24586b75a9d8STakashi Iwai static int snd_intel8x0_ali_chip_init(struct intel8x0 *chip, int probing)
24591da177e4SLinus Torvalds {
24601da177e4SLinus Torvalds u32 reg;
24611da177e4SLinus Torvalds int i = 0;
24621da177e4SLinus Torvalds
24631da177e4SLinus Torvalds reg = igetdword(chip, ICHREG(ALI_SCR));
24641da177e4SLinus Torvalds if ((reg & 2) == 0) /* Cold required */
24651da177e4SLinus Torvalds reg |= 2;
24661da177e4SLinus Torvalds else
24671da177e4SLinus Torvalds reg |= 1; /* Warm */
24681da177e4SLinus Torvalds reg &= ~0x80000000; /* ACLink on */
24691da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_SCR), reg);
24701da177e4SLinus Torvalds
24711da177e4SLinus Torvalds for (i = 0; i < HZ / 2; i++) {
24721da177e4SLinus Torvalds if (! (igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & ALI_INT_GPIO))
24731da177e4SLinus Torvalds goto __ok;
2474954bea35STakashi Iwai schedule_timeout_uninterruptible(1);
24751da177e4SLinus Torvalds }
2476f493e7bcSTakashi Iwai dev_err(chip->card->dev, "AC'97 reset failed.\n");
24771da177e4SLinus Torvalds if (probing)
24781da177e4SLinus Torvalds return -EIO;
24791da177e4SLinus Torvalds
24801da177e4SLinus Torvalds __ok:
24811da177e4SLinus Torvalds for (i = 0; i < HZ / 2; i++) {
24821da177e4SLinus Torvalds reg = igetdword(chip, ICHREG(ALI_RTSR));
24831da177e4SLinus Torvalds if (reg & 0x80) /* primary codec */
24841da177e4SLinus Torvalds break;
24851da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_RTSR), reg | 0x80);
2486954bea35STakashi Iwai schedule_timeout_uninterruptible(1);
24871da177e4SLinus Torvalds }
24881da177e4SLinus Torvalds
24891da177e4SLinus Torvalds do_ali_reset(chip);
24901da177e4SLinus Torvalds return 0;
24911da177e4SLinus Torvalds }
24921da177e4SLinus Torvalds
snd_intel8x0_chip_init(struct intel8x0 * chip,int probing)24936b75a9d8STakashi Iwai static int snd_intel8x0_chip_init(struct intel8x0 *chip, int probing)
24941da177e4SLinus Torvalds {
2495253b999fSJaroslav Kysela unsigned int i, timeout;
24961da177e4SLinus Torvalds int err;
24971da177e4SLinus Torvalds
24981da177e4SLinus Torvalds if (chip->device_type != DEVICE_ALI) {
24993a5f3dd3STakashi Iwai err = snd_intel8x0_ich_chip_init(chip, probing);
25003a5f3dd3STakashi Iwai if (err < 0)
25011da177e4SLinus Torvalds return err;
25021da177e4SLinus Torvalds iagetword(chip, 0); /* clear semaphore flag */
25031da177e4SLinus Torvalds } else {
25043a5f3dd3STakashi Iwai err = snd_intel8x0_ali_chip_init(chip, probing);
25053a5f3dd3STakashi Iwai if (err < 0)
25061da177e4SLinus Torvalds return err;
25071da177e4SLinus Torvalds }
25081da177e4SLinus Torvalds
25091da177e4SLinus Torvalds /* disable interrupts */
25101da177e4SLinus Torvalds for (i = 0; i < chip->bdbars_count; i++)
25111da177e4SLinus Torvalds iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00);
25121da177e4SLinus Torvalds /* reset channels */
25131da177e4SLinus Torvalds for (i = 0; i < chip->bdbars_count; i++)
25141da177e4SLinus Torvalds iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
2515253b999fSJaroslav Kysela for (i = 0; i < chip->bdbars_count; i++) {
2516253b999fSJaroslav Kysela timeout = 100000;
2517253b999fSJaroslav Kysela while (--timeout != 0) {
2518253b999fSJaroslav Kysela if ((igetbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset) & ICH_RESETREGS) == 0)
2519253b999fSJaroslav Kysela break;
2520253b999fSJaroslav Kysela }
2521253b999fSJaroslav Kysela if (timeout == 0)
2522f493e7bcSTakashi Iwai dev_err(chip->card->dev, "reset of registers failed?\n");
2523253b999fSJaroslav Kysela }
25241da177e4SLinus Torvalds /* initialize Buffer Descriptor Lists */
25251da177e4SLinus Torvalds for (i = 0; i < chip->bdbars_count; i++)
25266b75a9d8STakashi Iwai iputdword(chip, ICH_REG_OFF_BDBAR + chip->ichd[i].reg_offset,
25276b75a9d8STakashi Iwai chip->ichd[i].bdbar_addr);
25281da177e4SLinus Torvalds return 0;
25291da177e4SLinus Torvalds }
25301da177e4SLinus Torvalds
snd_intel8x0_free(struct snd_card * card)25317835e090STakashi Iwai static void snd_intel8x0_free(struct snd_card *card)
25321da177e4SLinus Torvalds {
25337835e090STakashi Iwai struct intel8x0 *chip = card->private_data;
25341da177e4SLinus Torvalds unsigned int i;
25351da177e4SLinus Torvalds
25361da177e4SLinus Torvalds if (chip->irq < 0)
25371da177e4SLinus Torvalds goto __hw_end;
25381da177e4SLinus Torvalds /* disable interrupts */
25391da177e4SLinus Torvalds for (i = 0; i < chip->bdbars_count; i++)
25401da177e4SLinus Torvalds iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00);
25411da177e4SLinus Torvalds /* reset channels */
25421da177e4SLinus Torvalds for (i = 0; i < chip->bdbars_count; i++)
25431da177e4SLinus Torvalds iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
2544a9e99660STakashi Iwai if (chip->device_type == DEVICE_NFORCE && !spdif_aclink) {
25451da177e4SLinus Torvalds /* stop the spdif interrupt */
25461da177e4SLinus Torvalds unsigned int val;
25471da177e4SLinus Torvalds pci_read_config_dword(chip->pci, 0x4c, &val);
25481da177e4SLinus Torvalds val &= ~0x1000000;
25491da177e4SLinus Torvalds pci_write_config_dword(chip->pci, 0x4c, val);
25501da177e4SLinus Torvalds }
25511da177e4SLinus Torvalds /* --- */
2552f000fd80SJeff Garzik
25531da177e4SLinus Torvalds __hw_end:
25541da177e4SLinus Torvalds if (chip->irq >= 0)
25556b75a9d8STakashi Iwai free_irq(chip->irq, chip);
25561da177e4SLinus Torvalds }
25571da177e4SLinus Torvalds
2558c7561cd8STakashi Iwai #ifdef CONFIG_PM_SLEEP
25591da177e4SLinus Torvalds /*
25601da177e4SLinus Torvalds * power management
25611da177e4SLinus Torvalds */
intel8x0_suspend(struct device * dev)256268cb2b55STakashi Iwai static int intel8x0_suspend(struct device *dev)
25631da177e4SLinus Torvalds {
256468cb2b55STakashi Iwai struct snd_card *card = dev_get_drvdata(dev);
25655809c6c4STakashi Iwai struct intel8x0 *chip = card->private_data;
25661da177e4SLinus Torvalds int i;
25671da177e4SLinus Torvalds
25685809c6c4STakashi Iwai snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
256984a43bd5STakashi Iwai for (i = 0; i < chip->ncodecs; i++)
25701da177e4SLinus Torvalds snd_ac97_suspend(chip->ac97[i]);
257152b72388STakashi Iwai if (chip->device_type == DEVICE_INTEL_ICH4)
257252b72388STakashi Iwai chip->sdm_saved = igetbyte(chip, ICHREG(SDM));
2573adbedd34SLen Brown
257430b35399STakashi Iwai if (chip->irq >= 0) {
25756b75a9d8STakashi Iwai free_irq(chip->irq, chip);
257630b35399STakashi Iwai chip->irq = -1;
2577be1391c7STakashi Iwai card->sync_irq = -1;
257830b35399STakashi Iwai }
25791da177e4SLinus Torvalds return 0;
25801da177e4SLinus Torvalds }
25811da177e4SLinus Torvalds
intel8x0_resume(struct device * dev)258268cb2b55STakashi Iwai static int intel8x0_resume(struct device *dev)
25831da177e4SLinus Torvalds {
258468cb2b55STakashi Iwai struct pci_dev *pci = to_pci_dev(dev);
258568cb2b55STakashi Iwai struct snd_card *card = dev_get_drvdata(dev);
25865809c6c4STakashi Iwai struct intel8x0 *chip = card->private_data;
25871da177e4SLinus Torvalds int i;
25881da177e4SLinus Torvalds
25892078f38cSTakashi Iwai snd_intel8x0_chip_init(chip, 0);
259030b35399STakashi Iwai if (request_irq(pci->irq, snd_intel8x0_interrupt,
2591934c2b6dSTakashi Iwai IRQF_SHARED, KBUILD_MODNAME, chip)) {
2592f493e7bcSTakashi Iwai dev_err(dev, "unable to grab IRQ %d, disabling device\n",
2593f493e7bcSTakashi Iwai pci->irq);
259430b35399STakashi Iwai snd_card_disconnect(card);
259530b35399STakashi Iwai return -EIO;
259630b35399STakashi Iwai }
25975809c6c4STakashi Iwai chip->irq = pci->irq;
2598be1391c7STakashi Iwai card->sync_irq = chip->irq;
25991da177e4SLinus Torvalds
260052b72388STakashi Iwai /* re-initialize mixer stuff */
2601a9e99660STakashi Iwai if (chip->device_type == DEVICE_INTEL_ICH4 && !spdif_aclink) {
260252b72388STakashi Iwai /* enable separate SDINs for ICH4 */
260352b72388STakashi Iwai iputbyte(chip, ICHREG(SDM), chip->sdm_saved);
260452b72388STakashi Iwai /* use slot 10/11 for SPDIF */
260552b72388STakashi Iwai iputdword(chip, ICHREG(GLOB_CNT),
260652b72388STakashi Iwai (igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_PCM_SPDIF_MASK) |
260752b72388STakashi Iwai ICH_PCM_SPDIF_1011);
260852b72388STakashi Iwai }
260952b72388STakashi Iwai
261084a43bd5STakashi Iwai for (i = 0; i < chip->ncodecs; i++)
26111da177e4SLinus Torvalds snd_ac97_resume(chip->ac97[i]);
26121da177e4SLinus Torvalds
26131cfe43d2STakashi Iwai /* resume status */
26141cfe43d2STakashi Iwai for (i = 0; i < chip->bdbars_count; i++) {
26156b75a9d8STakashi Iwai struct ichdev *ichdev = &chip->ichd[i];
26161cfe43d2STakashi Iwai unsigned long port = ichdev->reg_offset;
26171cfe43d2STakashi Iwai if (! ichdev->substream || ! ichdev->suspended)
26181cfe43d2STakashi Iwai continue;
26191cfe43d2STakashi Iwai if (ichdev->ichd == ICHD_PCMOUT)
26201cfe43d2STakashi Iwai snd_intel8x0_setup_pcm_out(chip, ichdev->substream->runtime);
26211cfe43d2STakashi Iwai iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
26221cfe43d2STakashi Iwai iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi);
26231cfe43d2STakashi Iwai iputbyte(chip, port + ICH_REG_OFF_CIV, ichdev->civ);
26241cfe43d2STakashi Iwai iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
26251cfe43d2STakashi Iwai }
26261cfe43d2STakashi Iwai
26275809c6c4STakashi Iwai snd_power_change_state(card, SNDRV_CTL_POWER_D0);
26281da177e4SLinus Torvalds return 0;
26291da177e4SLinus Torvalds }
263068cb2b55STakashi Iwai
263168cb2b55STakashi Iwai static SIMPLE_DEV_PM_OPS(intel8x0_pm, intel8x0_suspend, intel8x0_resume);
263268cb2b55STakashi Iwai #define INTEL8X0_PM_OPS &intel8x0_pm
263368cb2b55STakashi Iwai #else
263468cb2b55STakashi Iwai #define INTEL8X0_PM_OPS NULL
2635c7561cd8STakashi Iwai #endif /* CONFIG_PM_SLEEP */
26361da177e4SLinus Torvalds
26371da177e4SLinus Torvalds #define INTEL8X0_TESTBUF_SIZE 32768 /* enough large for one shot */
26381da177e4SLinus Torvalds
intel8x0_measure_ac97_clock(struct intel8x0 * chip)2639e23e7a14SBill Pemberton static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
26401da177e4SLinus Torvalds {
26416b75a9d8STakashi Iwai struct snd_pcm_substream *subs;
26426b75a9d8STakashi Iwai struct ichdev *ichdev;
26431da177e4SLinus Torvalds unsigned long port;
2644920e4ae3SJaroslav Kysela unsigned long pos, pos1, t;
26452ec775e7SJaroslav Kysela int civ, timeout = 1000, attempt = 1;
26462afe8be8SThomas Gleixner ktime_t start_time, stop_time;
26471da177e4SLinus Torvalds
26481da177e4SLinus Torvalds if (chip->ac97_bus->clock != 48000)
26491da177e4SLinus Torvalds return; /* specified in module option */
265027757876STakashi Iwai if (chip->inside_vm && !ac97_clock)
265127757876STakashi Iwai return; /* no measurement on VM */
26521da177e4SLinus Torvalds
26532ec775e7SJaroslav Kysela __again:
26541da177e4SLinus Torvalds subs = chip->pcm[0]->streams[0].substream;
26551da177e4SLinus Torvalds if (! subs || subs->dma_buffer.bytes < INTEL8X0_TESTBUF_SIZE) {
2656f493e7bcSTakashi Iwai dev_warn(chip->card->dev,
2657f493e7bcSTakashi Iwai "no playback buffer allocated - aborting measure ac97 clock\n");
26581da177e4SLinus Torvalds return;
26591da177e4SLinus Torvalds }
26601da177e4SLinus Torvalds ichdev = &chip->ichd[ICHD_PCMOUT];
26611da177e4SLinus Torvalds ichdev->physbuf = subs->dma_buffer.addr;
266229dab4fdSJaroslav Kysela ichdev->size = ichdev->fragsize = INTEL8X0_TESTBUF_SIZE;
26631da177e4SLinus Torvalds ichdev->substream = NULL; /* don't process interrupts */
26641da177e4SLinus Torvalds
26651da177e4SLinus Torvalds /* set rate */
26661da177e4SLinus Torvalds if (snd_ac97_set_rate(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 48000) < 0) {
2667f493e7bcSTakashi Iwai dev_err(chip->card->dev, "cannot set ac97 rate: clock = %d\n",
2668f493e7bcSTakashi Iwai chip->ac97_bus->clock);
26691da177e4SLinus Torvalds return;
26701da177e4SLinus Torvalds }
26711da177e4SLinus Torvalds snd_intel8x0_setup_periods(chip, ichdev);
26721da177e4SLinus Torvalds port = ichdev->reg_offset;
26731da177e4SLinus Torvalds spin_lock_irq(&chip->reg_lock);
26741da177e4SLinus Torvalds chip->in_measurement = 1;
26751da177e4SLinus Torvalds /* trigger */
26761da177e4SLinus Torvalds if (chip->device_type != DEVICE_ALI)
26771da177e4SLinus Torvalds iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM);
26781da177e4SLinus Torvalds else {
26791da177e4SLinus Torvalds iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
26801da177e4SLinus Torvalds iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
26811da177e4SLinus Torvalds }
26822afe8be8SThomas Gleixner start_time = ktime_get();
26831da177e4SLinus Torvalds spin_unlock_irq(&chip->reg_lock);
2684ef21ca24SNishanth Aravamudan msleep(50);
26851da177e4SLinus Torvalds spin_lock_irq(&chip->reg_lock);
26861da177e4SLinus Torvalds /* check the position */
2687920e4ae3SJaroslav Kysela do {
2688920e4ae3SJaroslav Kysela civ = igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV);
2689920e4ae3SJaroslav Kysela pos1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
2690920e4ae3SJaroslav Kysela if (pos1 == 0) {
2691920e4ae3SJaroslav Kysela udelay(10);
2692920e4ae3SJaroslav Kysela continue;
2693920e4ae3SJaroslav Kysela }
2694920e4ae3SJaroslav Kysela if (civ == igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV) &&
2695920e4ae3SJaroslav Kysela pos1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
2696920e4ae3SJaroslav Kysela break;
2697920e4ae3SJaroslav Kysela } while (timeout--);
2698da2436a2SJaroslav Kysela if (pos1 == 0) { /* oops, this value is not reliable */
2699da2436a2SJaroslav Kysela pos = 0;
2700da2436a2SJaroslav Kysela } else {
27011da177e4SLinus Torvalds pos = ichdev->fragsize1;
2702920e4ae3SJaroslav Kysela pos -= pos1 << ichdev->pos_shift;
27031da177e4SLinus Torvalds pos += ichdev->position;
2704da2436a2SJaroslav Kysela }
27051da177e4SLinus Torvalds chip->in_measurement = 0;
27062afe8be8SThomas Gleixner stop_time = ktime_get();
27071da177e4SLinus Torvalds /* stop */
27081da177e4SLinus Torvalds if (chip->device_type == DEVICE_ALI) {
2709d78bec21SWei Ni iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 16));
27101da177e4SLinus Torvalds iputbyte(chip, port + ICH_REG_OFF_CR, 0);
27111da177e4SLinus Torvalds while (igetbyte(chip, port + ICH_REG_OFF_CR))
27121da177e4SLinus Torvalds ;
27131da177e4SLinus Torvalds } else {
27141da177e4SLinus Torvalds iputbyte(chip, port + ICH_REG_OFF_CR, 0);
27151da177e4SLinus Torvalds while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH))
27161da177e4SLinus Torvalds ;
27171da177e4SLinus Torvalds }
27181da177e4SLinus Torvalds iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
27191da177e4SLinus Torvalds spin_unlock_irq(&chip->reg_lock);
27201da177e4SLinus Torvalds
2721da2436a2SJaroslav Kysela if (pos == 0) {
2722f493e7bcSTakashi Iwai dev_err(chip->card->dev,
2723f493e7bcSTakashi Iwai "measure - unreliable DMA position..\n");
27242ec775e7SJaroslav Kysela __retry:
272530fd9940SJaroslav Kysela if (attempt < 3) {
272630fd9940SJaroslav Kysela msleep(300);
27272ec775e7SJaroslav Kysela attempt++;
27282ec775e7SJaroslav Kysela goto __again;
27292ec775e7SJaroslav Kysela }
273030fd9940SJaroslav Kysela goto __end;
2731da2436a2SJaroslav Kysela }
2732da2436a2SJaroslav Kysela
2733920e4ae3SJaroslav Kysela pos /= 4;
27342afe8be8SThomas Gleixner t = ktime_us_delta(stop_time, start_time);
2735f493e7bcSTakashi Iwai dev_info(chip->card->dev,
2736f493e7bcSTakashi Iwai "%s: measured %lu usecs (%lu samples)\n", __func__, t, pos);
27371da177e4SLinus Torvalds if (t == 0) {
2738f493e7bcSTakashi Iwai dev_err(chip->card->dev, "?? calculation error..\n");
27392ec775e7SJaroslav Kysela goto __retry;
27401da177e4SLinus Torvalds }
2741920e4ae3SJaroslav Kysela pos *= 1000;
27421da177e4SLinus Torvalds pos = (pos / t) * 1000 + ((pos % t) * 1000) / t;
27432ec775e7SJaroslav Kysela if (pos < 40000 || pos >= 60000) {
27441da177e4SLinus Torvalds /* abnormal value. hw problem? */
2745f493e7bcSTakashi Iwai dev_info(chip->card->dev, "measured clock %ld rejected\n", pos);
27462ec775e7SJaroslav Kysela goto __retry;
27472ec775e7SJaroslav Kysela } else if (pos > 40500 && pos < 41500)
2748920e4ae3SJaroslav Kysela /* first exception - 41000Hz reference clock */
2749920e4ae3SJaroslav Kysela chip->ac97_bus->clock = 41000;
275029dab4fdSJaroslav Kysela else if (pos > 43600 && pos < 44600)
2751920e4ae3SJaroslav Kysela /* second exception - 44100HZ reference clock */
2752920e4ae3SJaroslav Kysela chip->ac97_bus->clock = 44100;
27531da177e4SLinus Torvalds else if (pos < 47500 || pos > 48500)
27541da177e4SLinus Torvalds /* not 48000Hz, tuning the clock.. */
27551da177e4SLinus Torvalds chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos;
275630fd9940SJaroslav Kysela __end:
2757f493e7bcSTakashi Iwai dev_info(chip->card->dev, "clocking to %d\n", chip->ac97_bus->clock);
27586dbe6628STakashi Iwai snd_ac97_update_power(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 0);
27591da177e4SLinus Torvalds }
27601da177e4SLinus Torvalds
276188e540a8STakashi Iwai static const struct snd_pci_quirk intel8x0_clock_list[] = {
2762d695e4eaSTakashi Iwai SND_PCI_QUIRK(0x0e11, 0x008a, "AD1885", 41000),
27634061db03SVittorio Gambaletta (VittGam) SND_PCI_QUIRK(0x1014, 0x0581, "AD1981B", 48000),
2764d695e4eaSTakashi Iwai SND_PCI_QUIRK(0x1028, 0x00be, "AD1885", 44100),
2765d695e4eaSTakashi Iwai SND_PCI_QUIRK(0x1028, 0x0177, "AD1980", 48000),
276678fad343SBastien Nocera SND_PCI_QUIRK(0x1028, 0x01ad, "AD1981B", 48000),
2767d695e4eaSTakashi Iwai SND_PCI_QUIRK(0x1043, 0x80f3, "AD1985", 48000),
2768d695e4eaSTakashi Iwai { } /* terminator */
27692b3b5485SJaroslav Kysela };
27702b3b5485SJaroslav Kysela
intel8x0_in_clock_list(struct intel8x0 * chip)2771e23e7a14SBill Pemberton static int intel8x0_in_clock_list(struct intel8x0 *chip)
27722b3b5485SJaroslav Kysela {
27732b3b5485SJaroslav Kysela struct pci_dev *pci = chip->pci;
2774d695e4eaSTakashi Iwai const struct snd_pci_quirk *wl;
27752b3b5485SJaroslav Kysela
2776d695e4eaSTakashi Iwai wl = snd_pci_quirk_lookup(pci, intel8x0_clock_list);
2777d695e4eaSTakashi Iwai if (!wl)
27782b3b5485SJaroslav Kysela return 0;
27797ce78fc8STakashi Iwai dev_info(chip->card->dev, "allow list rate for %04x:%04x is %i\n",
2780d695e4eaSTakashi Iwai pci->subsystem_vendor, pci->subsystem_device, wl->value);
2781d695e4eaSTakashi Iwai chip->ac97_bus->clock = wl->value;
2782d695e4eaSTakashi Iwai return 1;
27832b3b5485SJaroslav Kysela }
27842b3b5485SJaroslav Kysela
snd_intel8x0_proc_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)27856b75a9d8STakashi Iwai static void snd_intel8x0_proc_read(struct snd_info_entry * entry,
27866b75a9d8STakashi Iwai struct snd_info_buffer *buffer)
27871da177e4SLinus Torvalds {
27886b75a9d8STakashi Iwai struct intel8x0 *chip = entry->private_data;
27891da177e4SLinus Torvalds unsigned int tmp;
27901da177e4SLinus Torvalds
27911da177e4SLinus Torvalds snd_iprintf(buffer, "Intel8x0\n\n");
27921da177e4SLinus Torvalds if (chip->device_type == DEVICE_ALI)
27931da177e4SLinus Torvalds return;
27941da177e4SLinus Torvalds tmp = igetdword(chip, ICHREG(GLOB_STA));
27951da177e4SLinus Torvalds snd_iprintf(buffer, "Global control : 0x%08x\n", igetdword(chip, ICHREG(GLOB_CNT)));
27961da177e4SLinus Torvalds snd_iprintf(buffer, "Global status : 0x%08x\n", tmp);
27971da177e4SLinus Torvalds if (chip->device_type == DEVICE_INTEL_ICH4)
27981da177e4SLinus Torvalds snd_iprintf(buffer, "SDM : 0x%08x\n", igetdword(chip, ICHREG(SDM)));
279984a43bd5STakashi Iwai snd_iprintf(buffer, "AC'97 codecs ready :");
280084a43bd5STakashi Iwai if (tmp & chip->codec_isr_bits) {
280184a43bd5STakashi Iwai int i;
280284a43bd5STakashi Iwai static const char *codecs[3] = {
280384a43bd5STakashi Iwai "primary", "secondary", "tertiary"
280484a43bd5STakashi Iwai };
280584a43bd5STakashi Iwai for (i = 0; i < chip->max_codecs; i++)
280684a43bd5STakashi Iwai if (tmp & chip->codec_bit[i])
280784a43bd5STakashi Iwai snd_iprintf(buffer, " %s", codecs[i]);
280884a43bd5STakashi Iwai } else
280984a43bd5STakashi Iwai snd_iprintf(buffer, " none");
281084a43bd5STakashi Iwai snd_iprintf(buffer, "\n");
281184a43bd5STakashi Iwai if (chip->device_type == DEVICE_INTEL_ICH4 ||
281284a43bd5STakashi Iwai chip->device_type == DEVICE_SIS)
28131da177e4SLinus Torvalds snd_iprintf(buffer, "AC'97 codecs SDIN : %i %i %i\n",
28141da177e4SLinus Torvalds chip->ac97_sdin[0],
28151da177e4SLinus Torvalds chip->ac97_sdin[1],
28161da177e4SLinus Torvalds chip->ac97_sdin[2]);
28171da177e4SLinus Torvalds }
28181da177e4SLinus Torvalds
snd_intel8x0_proc_init(struct intel8x0 * chip)2819e23e7a14SBill Pemberton static void snd_intel8x0_proc_init(struct intel8x0 *chip)
28201da177e4SLinus Torvalds {
282147f2769bSTakashi Iwai snd_card_ro_proc_new(chip->card, "intel8x0", chip,
282247f2769bSTakashi Iwai snd_intel8x0_proc_read);
28231da177e4SLinus Torvalds }
28241da177e4SLinus Torvalds
28251da177e4SLinus Torvalds struct ich_reg_info {
28261da177e4SLinus Torvalds unsigned int int_sta_mask;
28271da177e4SLinus Torvalds unsigned int offset;
28281da177e4SLinus Torvalds };
28291da177e4SLinus Torvalds
2830f729f88aSTakashi Iwai static const unsigned int ich_codec_bits[3] = {
283184a43bd5STakashi Iwai ICH_PCR, ICH_SCR, ICH_TCR
283284a43bd5STakashi Iwai };
2833f729f88aSTakashi Iwai static const unsigned int sis_codec_bits[3] = {
283484a43bd5STakashi Iwai ICH_PCR, ICH_SCR, ICH_SIS_TCR
283584a43bd5STakashi Iwai };
283684a43bd5STakashi Iwai
snd_intel8x0_inside_vm(struct pci_dev * pci)2837e23e7a14SBill Pemberton static int snd_intel8x0_inside_vm(struct pci_dev *pci)
283865c397d6SKonstantin Ozerkov {
283965c397d6SKonstantin Ozerkov int result = inside_vm;
28407fb4f392SKonstantin Ozerkov char *msg = NULL;
284165c397d6SKonstantin Ozerkov
28427fb4f392SKonstantin Ozerkov /* check module parameter first (override detection) */
28437fb4f392SKonstantin Ozerkov if (result >= 0) {
28447fb4f392SKonstantin Ozerkov msg = result ? "enable (forced) VM" : "disable (forced) VM";
28457fb4f392SKonstantin Ozerkov goto fini;
284665c397d6SKonstantin Ozerkov }
284765c397d6SKonstantin Ozerkov
28487fb4f392SKonstantin Ozerkov /* check for known (emulated) devices */
28494926c804STakashi Iwai result = 0;
2850caf02abfSRobin H. Johnson if (pci->subsystem_vendor == PCI_SUBVENDOR_ID_REDHAT_QUMRANET &&
2851caf02abfSRobin H. Johnson pci->subsystem_device == PCI_SUBDEVICE_ID_QEMU) {
28527fb4f392SKonstantin Ozerkov /* KVM emulated sound, PCI SSID: 1af4:1100 */
28537fb4f392SKonstantin Ozerkov msg = "enable KVM";
28544926c804STakashi Iwai result = 1;
28557fb4f392SKonstantin Ozerkov } else if (pci->subsystem_vendor == 0x1ab8) {
28567fb4f392SKonstantin Ozerkov /* Parallels VM emulated sound, PCI SSID: 1ab8:xxxx */
28577fb4f392SKonstantin Ozerkov msg = "enable Parallels VM";
28584926c804STakashi Iwai result = 1;
28597fb4f392SKonstantin Ozerkov }
28607fb4f392SKonstantin Ozerkov
28617fb4f392SKonstantin Ozerkov fini:
28627fb4f392SKonstantin Ozerkov if (msg != NULL)
2863f493e7bcSTakashi Iwai dev_info(&pci->dev, "%s optimization\n", msg);
286465c397d6SKonstantin Ozerkov
286565c397d6SKonstantin Ozerkov return result;
286665c397d6SKonstantin Ozerkov }
286765c397d6SKonstantin Ozerkov
snd_intel8x0_init(struct snd_card * card,struct pci_dev * pci,unsigned long device_type)28687835e090STakashi Iwai static int snd_intel8x0_init(struct snd_card *card,
28691da177e4SLinus Torvalds struct pci_dev *pci,
28707835e090STakashi Iwai unsigned long device_type)
28711da177e4SLinus Torvalds {
28727835e090STakashi Iwai struct intel8x0 *chip = card->private_data;
28731da177e4SLinus Torvalds int err;
28741da177e4SLinus Torvalds unsigned int i;
28751da177e4SLinus Torvalds unsigned int int_sta_masks;
28766b75a9d8STakashi Iwai struct ichdev *ichdev;
28771da177e4SLinus Torvalds
2878f729f88aSTakashi Iwai static const unsigned int bdbars[] = {
28791da177e4SLinus Torvalds 3, /* DEVICE_INTEL */
28801da177e4SLinus Torvalds 6, /* DEVICE_INTEL_ICH4 */
28811da177e4SLinus Torvalds 3, /* DEVICE_SIS */
28821da177e4SLinus Torvalds 6, /* DEVICE_ALI */
28831da177e4SLinus Torvalds 4, /* DEVICE_NFORCE */
28841da177e4SLinus Torvalds };
2885f729f88aSTakashi Iwai static const struct ich_reg_info intel_regs[6] = {
28861da177e4SLinus Torvalds { ICH_PIINT, 0 },
28871da177e4SLinus Torvalds { ICH_POINT, 0x10 },
28881da177e4SLinus Torvalds { ICH_MCINT, 0x20 },
28891da177e4SLinus Torvalds { ICH_M2INT, 0x40 },
28901da177e4SLinus Torvalds { ICH_P2INT, 0x50 },
28911da177e4SLinus Torvalds { ICH_SPINT, 0x60 },
28921da177e4SLinus Torvalds };
2893f729f88aSTakashi Iwai static const struct ich_reg_info nforce_regs[4] = {
28941da177e4SLinus Torvalds { ICH_PIINT, 0 },
28951da177e4SLinus Torvalds { ICH_POINT, 0x10 },
28961da177e4SLinus Torvalds { ICH_MCINT, 0x20 },
28971da177e4SLinus Torvalds { ICH_NVSPINT, 0x70 },
28981da177e4SLinus Torvalds };
2899f729f88aSTakashi Iwai static const struct ich_reg_info ali_regs[6] = {
29001da177e4SLinus Torvalds { ALI_INT_PCMIN, 0x40 },
29011da177e4SLinus Torvalds { ALI_INT_PCMOUT, 0x50 },
29021da177e4SLinus Torvalds { ALI_INT_MICIN, 0x60 },
29031da177e4SLinus Torvalds { ALI_INT_CODECSPDIFOUT, 0x70 },
29041da177e4SLinus Torvalds { ALI_INT_SPDIFIN, 0xa0 },
29051da177e4SLinus Torvalds { ALI_INT_SPDIFOUT, 0xb0 },
29061da177e4SLinus Torvalds };
2907f729f88aSTakashi Iwai const struct ich_reg_info *tbl;
29081da177e4SLinus Torvalds
29097835e090STakashi Iwai err = pcim_enable_device(pci);
29103a5f3dd3STakashi Iwai if (err < 0)
29111da177e4SLinus Torvalds return err;
29121da177e4SLinus Torvalds
29131da177e4SLinus Torvalds spin_lock_init(&chip->reg_lock);
29141da177e4SLinus Torvalds chip->device_type = device_type;
29151da177e4SLinus Torvalds chip->card = card;
29161da177e4SLinus Torvalds chip->pci = pci;
29171da177e4SLinus Torvalds chip->irq = -1;
2918c829b052STakashi Iwai
2919c829b052STakashi Iwai /* module parameters */
2920c829b052STakashi Iwai chip->buggy_irq = buggy_irq;
2921c829b052STakashi Iwai chip->buggy_semaphore = buggy_semaphore;
2922c829b052STakashi Iwai if (xbox)
2923c829b052STakashi Iwai chip->xbox = 1;
29241da177e4SLinus Torvalds
29257fb4f392SKonstantin Ozerkov chip->inside_vm = snd_intel8x0_inside_vm(pci);
2926228cf793SKonstantin Ozerkov
29274985ddbfSTakashi Iwai /*
29284985ddbfSTakashi Iwai * Intel 82443MX running a 100MHz processor system bus has a hardware
29294985ddbfSTakashi Iwai * bug, which aborts PCI busmaster for audio transfer. A workaround
29304985ddbfSTakashi Iwai * is to set the pages as non-cached. For details, see the errata in
29314985ddbfSTakashi Iwai * http://download.intel.com/design/chipsets/specupdt/24505108.pdf
29324985ddbfSTakashi Iwai */
29331da177e4SLinus Torvalds if (pci->vendor == PCI_VENDOR_ID_INTEL &&
29341da177e4SLinus Torvalds pci->device == PCI_DEVICE_ID_INTEL_440MX)
29351da177e4SLinus Torvalds chip->fix_nocache = 1; /* enable workaround */
29361da177e4SLinus Torvalds
29373a5f3dd3STakashi Iwai err = pci_request_regions(pci, card->shortname);
29387835e090STakashi Iwai if (err < 0)
29391da177e4SLinus Torvalds return err;
29401da177e4SLinus Torvalds
29411da177e4SLinus Torvalds if (device_type == DEVICE_ALI) {
29421da177e4SLinus Torvalds /* ALI5455 has no ac97 region */
29437835e090STakashi Iwai chip->bmaddr = pcim_iomap(pci, 0, 0);
29447835e090STakashi Iwai } else {
29453388c37eSTakashi Iwai if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) /* ICH4 and Nforce */
29467835e090STakashi Iwai chip->addr = pcim_iomap(pci, 2, 0);
29473388c37eSTakashi Iwai else
29487835e090STakashi Iwai chip->addr = pcim_iomap(pci, 0, 0);
29493388c37eSTakashi Iwai if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) /* ICH4 */
29507835e090STakashi Iwai chip->bmaddr = pcim_iomap(pci, 3, 0);
29513388c37eSTakashi Iwai else
29527835e090STakashi Iwai chip->bmaddr = pcim_iomap(pci, 1, 0);
29531da177e4SLinus Torvalds }
29547835e090STakashi Iwai
29551da177e4SLinus Torvalds chip->bdbars_count = bdbars[device_type];
29561da177e4SLinus Torvalds
29571da177e4SLinus Torvalds /* initialize offsets */
29581da177e4SLinus Torvalds switch (device_type) {
29591da177e4SLinus Torvalds case DEVICE_NFORCE:
29601da177e4SLinus Torvalds tbl = nforce_regs;
29611da177e4SLinus Torvalds break;
29621da177e4SLinus Torvalds case DEVICE_ALI:
29631da177e4SLinus Torvalds tbl = ali_regs;
29641da177e4SLinus Torvalds break;
29651da177e4SLinus Torvalds default:
29661da177e4SLinus Torvalds tbl = intel_regs;
29671da177e4SLinus Torvalds break;
29681da177e4SLinus Torvalds }
29691da177e4SLinus Torvalds for (i = 0; i < chip->bdbars_count; i++) {
29701da177e4SLinus Torvalds ichdev = &chip->ichd[i];
29711da177e4SLinus Torvalds ichdev->ichd = i;
29721da177e4SLinus Torvalds ichdev->reg_offset = tbl[i].offset;
29731da177e4SLinus Torvalds ichdev->int_sta_mask = tbl[i].int_sta_mask;
29741da177e4SLinus Torvalds if (device_type == DEVICE_SIS) {
29751da177e4SLinus Torvalds /* SiS 7012 swaps the registers */
29761da177e4SLinus Torvalds ichdev->roff_sr = ICH_REG_OFF_PICB;
29771da177e4SLinus Torvalds ichdev->roff_picb = ICH_REG_OFF_SR;
29781da177e4SLinus Torvalds } else {
29791da177e4SLinus Torvalds ichdev->roff_sr = ICH_REG_OFF_SR;
29801da177e4SLinus Torvalds ichdev->roff_picb = ICH_REG_OFF_PICB;
29811da177e4SLinus Torvalds }
29821da177e4SLinus Torvalds if (device_type == DEVICE_ALI)
29831da177e4SLinus Torvalds ichdev->ali_slot = (ichdev->reg_offset - 0x40) / 0x10;
29841da177e4SLinus Torvalds /* SIS7012 handles the pcm data in bytes, others are in samples */
29851da177e4SLinus Torvalds ichdev->pos_shift = (device_type == DEVICE_SIS) ? 0 : 1;
29861da177e4SLinus Torvalds }
29871da177e4SLinus Torvalds
29881da177e4SLinus Torvalds /* allocate buffer descriptor lists */
29891da177e4SLinus Torvalds /* the start of each lists must be aligned to 8 bytes */
29907835e090STakashi Iwai chip->bdbars = snd_devm_alloc_pages(&pci->dev, intel8x0_dma_type(chip),
29917835e090STakashi Iwai chip->bdbars_count * sizeof(u32) *
29927835e090STakashi Iwai ICH_MAX_FRAGS * 2);
29937835e090STakashi Iwai if (!chip->bdbars)
29941da177e4SLinus Torvalds return -ENOMEM;
29957835e090STakashi Iwai
29961da177e4SLinus Torvalds /* tables must be aligned to 8 bytes here, but the kernel pages
29971da177e4SLinus Torvalds are much bigger, so we don't care (on i386) */
29981da177e4SLinus Torvalds int_sta_masks = 0;
29991da177e4SLinus Torvalds for (i = 0; i < chip->bdbars_count; i++) {
30001da177e4SLinus Torvalds ichdev = &chip->ichd[i];
30017835e090STakashi Iwai ichdev->bdbar = ((__le32 *)chip->bdbars->area) +
3002beef08a5STakashi Iwai (i * ICH_MAX_FRAGS * 2);
30037835e090STakashi Iwai ichdev->bdbar_addr = chip->bdbars->addr +
3004beef08a5STakashi Iwai (i * sizeof(u32) * ICH_MAX_FRAGS * 2);
30051da177e4SLinus Torvalds int_sta_masks |= ichdev->int_sta_mask;
30061da177e4SLinus Torvalds }
3007beef08a5STakashi Iwai chip->int_sta_reg = device_type == DEVICE_ALI ?
3008beef08a5STakashi Iwai ICH_REG_ALI_INTERRUPTSR : ICH_REG_GLOB_STA;
30091da177e4SLinus Torvalds chip->int_sta_mask = int_sta_masks;
30101da177e4SLinus Torvalds
3011beef08a5STakashi Iwai pci_set_master(pci);
3012beef08a5STakashi Iwai
301384a43bd5STakashi Iwai switch(chip->device_type) {
301484a43bd5STakashi Iwai case DEVICE_INTEL_ICH4:
301584a43bd5STakashi Iwai /* ICH4 can have three codecs */
301684a43bd5STakashi Iwai chip->max_codecs = 3;
301784a43bd5STakashi Iwai chip->codec_bit = ich_codec_bits;
301884a43bd5STakashi Iwai chip->codec_ready_bits = ICH_PRI | ICH_SRI | ICH_TRI;
301984a43bd5STakashi Iwai break;
302084a43bd5STakashi Iwai case DEVICE_SIS:
302184a43bd5STakashi Iwai /* recent SIS7012 can have three codecs */
302284a43bd5STakashi Iwai chip->max_codecs = 3;
302384a43bd5STakashi Iwai chip->codec_bit = sis_codec_bits;
302484a43bd5STakashi Iwai chip->codec_ready_bits = ICH_PRI | ICH_SRI | ICH_SIS_TRI;
302584a43bd5STakashi Iwai break;
302684a43bd5STakashi Iwai default:
302784a43bd5STakashi Iwai /* others up to two codecs */
302884a43bd5STakashi Iwai chip->max_codecs = 2;
302984a43bd5STakashi Iwai chip->codec_bit = ich_codec_bits;
303084a43bd5STakashi Iwai chip->codec_ready_bits = ICH_PRI | ICH_SRI;
303184a43bd5STakashi Iwai break;
303284a43bd5STakashi Iwai }
303384a43bd5STakashi Iwai for (i = 0; i < chip->max_codecs; i++)
303484a43bd5STakashi Iwai chip->codec_isr_bits |= chip->codec_bit[i];
303584a43bd5STakashi Iwai
30363a5f3dd3STakashi Iwai err = snd_intel8x0_chip_init(chip, 1);
30377835e090STakashi Iwai if (err < 0)
30381da177e4SLinus Torvalds return err;
30391da177e4SLinus Torvalds
30402078f38cSTakashi Iwai /* request irq after initializaing int_sta_mask, etc */
30417835e090STakashi Iwai /* NOTE: we don't use devm version here since it's released /
30427835e090STakashi Iwai * re-acquired in PM callbacks.
30437835e090STakashi Iwai * It's released explicitly in snd_intel8x0_free(), too.
30447835e090STakashi Iwai */
30452078f38cSTakashi Iwai if (request_irq(pci->irq, snd_intel8x0_interrupt,
3046934c2b6dSTakashi Iwai IRQF_SHARED, KBUILD_MODNAME, chip)) {
3047f493e7bcSTakashi Iwai dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq);
30482078f38cSTakashi Iwai return -EBUSY;
30492078f38cSTakashi Iwai }
30502078f38cSTakashi Iwai chip->irq = pci->irq;
3051be1391c7STakashi Iwai card->sync_irq = chip->irq;
30522078f38cSTakashi Iwai
30537835e090STakashi Iwai card->private_free = snd_intel8x0_free;
30541da177e4SLinus Torvalds
30551da177e4SLinus Torvalds return 0;
30561da177e4SLinus Torvalds }
30571da177e4SLinus Torvalds
30581da177e4SLinus Torvalds static struct shortname_table {
30591da177e4SLinus Torvalds unsigned int id;
30601da177e4SLinus Torvalds const char *s;
3061e23e7a14SBill Pemberton } shortnames[] = {
30628cdfd251STakashi Iwai { PCI_DEVICE_ID_INTEL_82801AA_5, "Intel 82801AA-ICH" },
30638cdfd251STakashi Iwai { PCI_DEVICE_ID_INTEL_82801AB_5, "Intel 82901AB-ICH0" },
30648cdfd251STakashi Iwai { PCI_DEVICE_ID_INTEL_82801BA_4, "Intel 82801BA-ICH2" },
30651da177e4SLinus Torvalds { PCI_DEVICE_ID_INTEL_440MX, "Intel 440MX" },
30668cdfd251STakashi Iwai { PCI_DEVICE_ID_INTEL_82801CA_5, "Intel 82801CA-ICH3" },
30678cdfd251STakashi Iwai { PCI_DEVICE_ID_INTEL_82801DB_5, "Intel 82801DB-ICH4" },
30688cdfd251STakashi Iwai { PCI_DEVICE_ID_INTEL_82801EB_5, "Intel ICH5" },
30691da177e4SLinus Torvalds { PCI_DEVICE_ID_INTEL_ESB_5, "Intel 6300ESB" },
30701da177e4SLinus Torvalds { PCI_DEVICE_ID_INTEL_ICH6_18, "Intel ICH6" },
30711da177e4SLinus Torvalds { PCI_DEVICE_ID_INTEL_ICH7_20, "Intel ICH7" },
30723437c5dfSJason Gaston { PCI_DEVICE_ID_INTEL_ESB2_14, "Intel ESB2" },
30731da177e4SLinus Torvalds { PCI_DEVICE_ID_SI_7012, "SiS SI7012" },
30748cdfd251STakashi Iwai { PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO, "NVidia nForce" },
30751da177e4SLinus Torvalds { PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO, "NVidia nForce2" },
30761da177e4SLinus Torvalds { PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO, "NVidia nForce3" },
30771da177e4SLinus Torvalds { PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO, "NVidia CK8S" },
30781da177e4SLinus Torvalds { PCI_DEVICE_ID_NVIDIA_CK804_AUDIO, "NVidia CK804" },
30791da177e4SLinus Torvalds { PCI_DEVICE_ID_NVIDIA_CK8_AUDIO, "NVidia CK8" },
30801da177e4SLinus Torvalds { 0x003a, "NVidia MCP04" },
30811da177e4SLinus Torvalds { 0x746d, "AMD AMD8111" },
30821da177e4SLinus Torvalds { 0x7445, "AMD AMD768" },
30831da177e4SLinus Torvalds { 0x5455, "ALi M5455" },
30841da177e4SLinus Torvalds { 0, NULL },
30851da177e4SLinus Torvalds };
30861da177e4SLinus Torvalds
308788e540a8STakashi Iwai static const struct snd_pci_quirk spdif_aclink_defaults[] = {
3088a9e99660STakashi Iwai SND_PCI_QUIRK(0x147b, 0x1c1a, "ASUS KN8", 1),
3089a9e99660STakashi Iwai { } /* end */
3090a9e99660STakashi Iwai };
3091a9e99660STakashi Iwai
30927ce78fc8STakashi Iwai /* look up allow/deny list for SPDIF over ac-link */
check_default_spdif_aclink(struct pci_dev * pci)3093e23e7a14SBill Pemberton static int check_default_spdif_aclink(struct pci_dev *pci)
3094a9e99660STakashi Iwai {
3095a9e99660STakashi Iwai const struct snd_pci_quirk *w;
3096a9e99660STakashi Iwai
3097a9e99660STakashi Iwai w = snd_pci_quirk_lookup(pci, spdif_aclink_defaults);
3098a9e99660STakashi Iwai if (w) {
3099a9e99660STakashi Iwai if (w->value)
3100f493e7bcSTakashi Iwai dev_dbg(&pci->dev,
3101f493e7bcSTakashi Iwai "Using SPDIF over AC-Link for %s\n",
310286b27237STakashi Iwai snd_pci_quirk_name(w));
3103a9e99660STakashi Iwai else
3104f493e7bcSTakashi Iwai dev_dbg(&pci->dev,
3105f493e7bcSTakashi Iwai "Using integrated SPDIF DMA for %s\n",
310686b27237STakashi Iwai snd_pci_quirk_name(w));
3107a9e99660STakashi Iwai return w->value;
3108a9e99660STakashi Iwai }
3109a9e99660STakashi Iwai return 0;
3110a9e99660STakashi Iwai }
3111a9e99660STakashi Iwai
__snd_intel8x0_probe(struct pci_dev * pci,const struct pci_device_id * pci_id)3112*71b21f5fSTakashi Iwai static int __snd_intel8x0_probe(struct pci_dev *pci,
31131da177e4SLinus Torvalds const struct pci_device_id *pci_id)
31141da177e4SLinus Torvalds {
31156b75a9d8STakashi Iwai struct snd_card *card;
31166b75a9d8STakashi Iwai struct intel8x0 *chip;
31171da177e4SLinus Torvalds int err;
31181da177e4SLinus Torvalds struct shortname_table *name;
31191da177e4SLinus Torvalds
31207835e090STakashi Iwai err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE,
31217835e090STakashi Iwai sizeof(*chip), &card);
3122e58de7baSTakashi Iwai if (err < 0)
3123e58de7baSTakashi Iwai return err;
31247835e090STakashi Iwai chip = card->private_data;
31251da177e4SLinus Torvalds
3126a9e99660STakashi Iwai if (spdif_aclink < 0)
3127a9e99660STakashi Iwai spdif_aclink = check_default_spdif_aclink(pci);
3128a9e99660STakashi Iwai
3129a9e99660STakashi Iwai strcpy(card->driver, "ICH");
3130a9e99660STakashi Iwai if (!spdif_aclink) {
31311da177e4SLinus Torvalds switch (pci_id->driver_data) {
31321da177e4SLinus Torvalds case DEVICE_NFORCE:
31331da177e4SLinus Torvalds strcpy(card->driver, "NFORCE");
31341da177e4SLinus Torvalds break;
31351da177e4SLinus Torvalds case DEVICE_INTEL_ICH4:
31361da177e4SLinus Torvalds strcpy(card->driver, "ICH4");
3137a9e99660STakashi Iwai }
31381da177e4SLinus Torvalds }
31391da177e4SLinus Torvalds
31401da177e4SLinus Torvalds strcpy(card->shortname, "Intel ICH");
31411da177e4SLinus Torvalds for (name = shortnames; name->id; name++) {
31421da177e4SLinus Torvalds if (pci->device == name->id) {
31431da177e4SLinus Torvalds strcpy(card->shortname, name->s);
31441da177e4SLinus Torvalds break;
31451da177e4SLinus Torvalds }
31461da177e4SLinus Torvalds }
31471da177e4SLinus Torvalds
3148beef08a5STakashi Iwai if (buggy_irq < 0) {
3149beef08a5STakashi Iwai /* some Nforce[2] and ICH boards have problems with IRQ handling.
3150beef08a5STakashi Iwai * Needs to return IRQ_HANDLED for unknown irqs.
3151beef08a5STakashi Iwai */
3152beef08a5STakashi Iwai if (pci_id->driver_data == DEVICE_NFORCE)
3153beef08a5STakashi Iwai buggy_irq = 1;
3154beef08a5STakashi Iwai else
3155beef08a5STakashi Iwai buggy_irq = 0;
3156beef08a5STakashi Iwai }
3157beef08a5STakashi Iwai
31587835e090STakashi Iwai err = snd_intel8x0_init(card, pci, pci_id->driver_data);
31597835e090STakashi Iwai if (err < 0)
31601da177e4SLinus Torvalds return err;
31611da177e4SLinus Torvalds
31623a5f3dd3STakashi Iwai err = snd_intel8x0_mixer(chip, ac97_clock, ac97_quirk);
31637835e090STakashi Iwai if (err < 0)
31641da177e4SLinus Torvalds return err;
31653a5f3dd3STakashi Iwai err = snd_intel8x0_pcm(chip);
31667835e090STakashi Iwai if (err < 0)
31671da177e4SLinus Torvalds return err;
31681da177e4SLinus Torvalds
31691da177e4SLinus Torvalds snd_intel8x0_proc_init(chip);
31701da177e4SLinus Torvalds
31711da177e4SLinus Torvalds snprintf(card->longname, sizeof(card->longname),
31723388c37eSTakashi Iwai "%s with %s at irq %i", card->shortname,
31733388c37eSTakashi Iwai snd_ac97_get_short_name(chip->ac97[0]), chip->irq);
31741da177e4SLinus Torvalds
31752b3b5485SJaroslav Kysela if (ac97_clock == 0 || ac97_clock == 1) {
31762b3b5485SJaroslav Kysela if (ac97_clock == 0) {
31772b3b5485SJaroslav Kysela if (intel8x0_in_clock_list(chip) == 0)
31781da177e4SLinus Torvalds intel8x0_measure_ac97_clock(chip);
31792b3b5485SJaroslav Kysela } else {
31802b3b5485SJaroslav Kysela intel8x0_measure_ac97_clock(chip);
31812b3b5485SJaroslav Kysela }
31822b3b5485SJaroslav Kysela }
31831da177e4SLinus Torvalds
31843a5f3dd3STakashi Iwai err = snd_card_register(card);
31857835e090STakashi Iwai if (err < 0)
31861da177e4SLinus Torvalds return err;
31877835e090STakashi Iwai
31881da177e4SLinus Torvalds pci_set_drvdata(pci, card);
31891da177e4SLinus Torvalds return 0;
31901da177e4SLinus Torvalds }
31911da177e4SLinus Torvalds
snd_intel8x0_probe(struct pci_dev * pci,const struct pci_device_id * pci_id)3192*71b21f5fSTakashi Iwai static int snd_intel8x0_probe(struct pci_dev *pci,
3193*71b21f5fSTakashi Iwai const struct pci_device_id *pci_id)
3194*71b21f5fSTakashi Iwai {
3195*71b21f5fSTakashi Iwai return snd_card_free_on_error(&pci->dev, __snd_intel8x0_probe(pci, pci_id));
3196*71b21f5fSTakashi Iwai }
3197*71b21f5fSTakashi Iwai
3198e9f66d9bSTakashi Iwai static struct pci_driver intel8x0_driver = {
31993733e424STakashi Iwai .name = KBUILD_MODNAME,
32001da177e4SLinus Torvalds .id_table = snd_intel8x0_ids,
32011da177e4SLinus Torvalds .probe = snd_intel8x0_probe,
320268cb2b55STakashi Iwai .driver = {
320368cb2b55STakashi Iwai .pm = INTEL8X0_PM_OPS,
320468cb2b55STakashi Iwai },
32051da177e4SLinus Torvalds };
32061da177e4SLinus Torvalds
3207e9f66d9bSTakashi Iwai module_pci_driver(intel8x0_driver);
3208