xref: /openbmc/linux/sound/pci/intel8x0.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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