xref: /linux/sound/isa/opti9xx/opti92x-ad1848.c (revision e24ef4881bdd11c13d35235a410571c0d0aa0e5d)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds     card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards.
41da177e4SLinus Torvalds     Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it>
51da177e4SLinus Torvalds 
61da177e4SLinus Torvalds     Part of this code was developed at the Italian Ministry of Air Defence,
71da177e4SLinus Torvalds     Sixth Division (oh, che pace ...), Rome.
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds     Thanks to Maria Grazia Pollarini, Salvatore Vassallo.
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds */
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #include <linux/init.h>
1599a0b768STakashi Iwai #include <linux/err.h>
165e24c1c1STakashi Iwai #include <linux/isa.h>
1799a0b768STakashi Iwai #include <linux/delay.h>
181da177e4SLinus Torvalds #include <linux/pnp.h>
1965a77217SPaul Gortmaker #include <linux/module.h>
206cbbfe1cSTakashi Iwai #include <linux/io.h>
2199a0b768STakashi Iwai #include <asm/dma.h>
221da177e4SLinus Torvalds #include <sound/core.h>
23b2e8d7daSKrzysztof Helt #include <sound/tlv.h>
2461ef19d7SKrzysztof Helt #include <sound/wss.h>
251da177e4SLinus Torvalds #include <sound/mpu401.h>
261da177e4SLinus Torvalds #include <sound/opl3.h>
271da177e4SLinus Torvalds #ifndef OPTi93X
281da177e4SLinus Torvalds #include <sound/opl4.h>
291da177e4SLinus Torvalds #endif
30988aec3dSOndrej Zary #define SNDRV_LEGACY_FIND_FREE_IOPORT
311da177e4SLinus Torvalds #define SNDRV_LEGACY_FIND_FREE_IRQ
321da177e4SLinus Torvalds #define SNDRV_LEGACY_FIND_FREE_DMA
331da177e4SLinus Torvalds #include <sound/initval.h>
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
361da177e4SLinus Torvalds MODULE_LICENSE("GPL");
371da177e4SLinus Torvalds #ifdef OPTi93X
381da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi93X");
391da177e4SLinus Torvalds #else	/* OPTi93X */
401da177e4SLinus Torvalds #ifdef CS4231
411da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi92X - CS4231");
421da177e4SLinus Torvalds #else	/* CS4231 */
431da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi92X - AD1848");
441da177e4SLinus Torvalds #endif	/* CS4231 */
451da177e4SLinus Torvalds #endif	/* OPTi93X */
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
481da177e4SLinus Torvalds static char *id = SNDRV_DEFAULT_STR1;		/* ID for this card */
49a67ff6a5SRusty Russell //static bool enable = SNDRV_DEFAULT_ENABLE1;	/* Enable this card */
5051f6baadSRene Herman #ifdef CONFIG_PNP
51c578ae00SRandy Dunlap static bool isapnp = true;			/* Enable ISA PnP detection */
5251f6baadSRene Herman #endif
531da177e4SLinus Torvalds static long port = SNDRV_DEFAULT_PORT1; 	/* 0x530,0xe80,0xf40,0x604 */
541da177e4SLinus Torvalds static long mpu_port = SNDRV_DEFAULT_PORT1;	/* 0x300,0x310,0x320,0x330 */
551da177e4SLinus Torvalds static long fm_port = SNDRV_DEFAULT_PORT1;	/* 0x388 */
561da177e4SLinus Torvalds static int irq = SNDRV_DEFAULT_IRQ1;		/* 5,7,9,10,11 */
571da177e4SLinus Torvalds static int mpu_irq = SNDRV_DEFAULT_IRQ1;	/* 5,7,9,10 */
581da177e4SLinus Torvalds static int dma1 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
591da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
601da177e4SLinus Torvalds static int dma2 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
611da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds module_param(index, int, 0444);
641da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for opti9xx based soundcard.");
651da177e4SLinus Torvalds module_param(id, charp, 0444);
661da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard.");
671da177e4SLinus Torvalds //module_param(enable, bool, 0444);
681da177e4SLinus Torvalds //MODULE_PARM_DESC(enable, "Enable opti9xx soundcard.");
6951f6baadSRene Herman #ifdef CONFIG_PNP
701da177e4SLinus Torvalds module_param(isapnp, bool, 0444);
711da177e4SLinus Torvalds MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
7251f6baadSRene Herman #endif
73e992ef57SDavid Howells module_param_hw(port, long, ioport, 0444);
741da177e4SLinus Torvalds MODULE_PARM_DESC(port, "WSS port # for opti9xx driver.");
75e992ef57SDavid Howells module_param_hw(mpu_port, long, ioport, 0444);
761da177e4SLinus Torvalds MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver.");
77e992ef57SDavid Howells module_param_hw(fm_port, long, ioport, 0444);
781da177e4SLinus Torvalds MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver.");
79e992ef57SDavid Howells module_param_hw(irq, int, irq, 0444);
801da177e4SLinus Torvalds MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver.");
81e992ef57SDavid Howells module_param_hw(mpu_irq, int, irq, 0444);
821da177e4SLinus Torvalds MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver.");
83e992ef57SDavid Howells module_param_hw(dma1, int, dma, 0444);
841da177e4SLinus Torvalds MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver.");
851da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
86e992ef57SDavid Howells module_param_hw(dma2, int, dma, 0444);
871da177e4SLinus Torvalds MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
881da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds #define OPTi9XX_HW_82C928	1
911da177e4SLinus Torvalds #define OPTi9XX_HW_82C929	2
921da177e4SLinus Torvalds #define OPTi9XX_HW_82C924	3
931da177e4SLinus Torvalds #define OPTi9XX_HW_82C925	4
941da177e4SLinus Torvalds #define OPTi9XX_HW_82C930	5
951da177e4SLinus Torvalds #define OPTi9XX_HW_82C931	6
961da177e4SLinus Torvalds #define OPTi9XX_HW_82C933	7
971da177e4SLinus Torvalds #define OPTi9XX_HW_LAST		OPTi9XX_HW_82C933
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds #define OPTi9XX_MC_REG(n)	n
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds #ifdef OPTi93X
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds #define OPTi93X_STATUS			0x02
1041da177e4SLinus Torvalds #define OPTi93X_PORT(chip, r)		((chip)->port + OPTi93X_##r)
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds #define OPTi93X_IRQ_PLAYBACK		0x04
1071da177e4SLinus Torvalds #define OPTi93X_IRQ_CAPTURE		0x08
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds #endif /* OPTi93X */
1101da177e4SLinus Torvalds 
111346c7a68STakashi Iwai struct snd_opti9xx {
1121da177e4SLinus Torvalds 	unsigned short hardware;
1131da177e4SLinus Torvalds 	unsigned char password;
1141da177e4SLinus Torvalds 	char name[7];
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 	unsigned long mc_base;
1171da177e4SLinus Torvalds 	struct resource *res_mc_base;
1181da177e4SLinus Torvalds 	unsigned long mc_base_size;
1191da177e4SLinus Torvalds #ifdef OPTi93X
1201da177e4SLinus Torvalds 	unsigned long mc_indir_index;
121e6960e19SKrzysztof Helt 	struct resource *res_mc_indir;
1221da177e4SLinus Torvalds #endif	/* OPTi93X */
1235dd25072SOndrej Zary 	struct snd_wss *codec;
1241da177e4SLinus Torvalds 	unsigned long pwd_reg;
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	spinlock_t lock;
1271da177e4SLinus Torvalds 
128fd8d4735SKrzysztof Helt 	long wss_base;
1291da177e4SLinus Torvalds 	int irq;
1301da177e4SLinus Torvalds };
1311da177e4SLinus Torvalds 
13299a0b768STakashi Iwai static int snd_opti9xx_pnp_is_probed;
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds #ifdef CONFIG_PNP
1351da177e4SLinus Torvalds 
136529d2576SArvind Yadav static const struct pnp_card_device_id snd_opti9xx_pnpids[] = {
1371da177e4SLinus Torvalds #ifndef OPTi93X
1381da177e4SLinus Torvalds 	/* OPTi 82C924 */
139fd8d4735SKrzysztof Helt 	{ .id = "OPT0924",
140fd8d4735SKrzysztof Helt 	  .devs = { { "OPT0000" }, { "OPT0002" }, { "OPT0005" } },
141fd8d4735SKrzysztof Helt 	  .driver_data = 0x0924 },
1421da177e4SLinus Torvalds 	/* OPTi 82C925 */
143fd8d4735SKrzysztof Helt 	{ .id = "OPT0925",
144fd8d4735SKrzysztof Helt 	  .devs = { { "OPT9250" }, { "OPT0002" }, { "OPT0005" } },
145fd8d4735SKrzysztof Helt 	  .driver_data = 0x0925 },
1461da177e4SLinus Torvalds #else
1471da177e4SLinus Torvalds 	/* OPTi 82C931/3 */
148fd8d4735SKrzysztof Helt 	{ .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } },
149fd8d4735SKrzysztof Helt 	  .driver_data = 0x0931 },
1501da177e4SLinus Torvalds #endif	/* OPTi93X */
1511da177e4SLinus Torvalds 	{ .id = "" }
1521da177e4SLinus Torvalds };
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids);
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds #endif	/* CONFIG_PNP */
1571da177e4SLinus Torvalds 
158fb615499STakashi Iwai #define DEV_NAME KBUILD_MODNAME
1591da177e4SLinus Torvalds 
16080e0a7c0STakashi Iwai static const char * const snd_opti9xx_names[] = {
161af901ca1SAndré Goddard Rosa 	"unknown",
1621da177e4SLinus Torvalds 	"82C928",	"82C929",
1631da177e4SLinus Torvalds 	"82C924",	"82C925",
1641da177e4SLinus Torvalds 	"82C930",	"82C931",	"82C933"
1651da177e4SLinus Torvalds };
1661da177e4SLinus Torvalds 
1671bff292eSBill Pemberton static int snd_opti9xx_init(struct snd_opti9xx *chip,
1685e24c1c1STakashi Iwai 			    unsigned short hardware)
1691da177e4SLinus Torvalds {
17080e0a7c0STakashi Iwai 	static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	chip->hardware = hardware;
1731da177e4SLinus Torvalds 	strcpy(chip->name, snd_opti9xx_names[hardware]);
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 	spin_lock_init(&chip->lock);
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	chip->irq = -1;
1781da177e4SLinus Torvalds 
179fd8d4735SKrzysztof Helt #ifndef OPTi93X
180fd8d4735SKrzysztof Helt #ifdef CONFIG_PNP
181fd8d4735SKrzysztof Helt 	if (isapnp && chip->mc_base)
182fd8d4735SKrzysztof Helt 		/* PnP resource gives the least 10 bits */
183fd8d4735SKrzysztof Helt 		chip->mc_base |= 0xc00;
18489c0ac7cSRandy Dunlap 	else
185fd8d4735SKrzysztof Helt #endif	/* CONFIG_PNP */
18689c0ac7cSRandy Dunlap 	{
187fd8d4735SKrzysztof Helt 		chip->mc_base = 0xf8c;
188fd8d4735SKrzysztof Helt 		chip->mc_base_size = opti9xx_mc_size[hardware];
189fd8d4735SKrzysztof Helt 	}
190fd8d4735SKrzysztof Helt #else
191fd8d4735SKrzysztof Helt 		chip->mc_base_size = opti9xx_mc_size[hardware];
192fd8d4735SKrzysztof Helt #endif
193fd8d4735SKrzysztof Helt 
1941da177e4SLinus Torvalds 	switch (hardware) {
1951da177e4SLinus Torvalds #ifndef OPTi93X
1961da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
1971da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
1981da177e4SLinus Torvalds 		chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3;
1991da177e4SLinus Torvalds 		chip->pwd_reg = 3;
2001da177e4SLinus Torvalds 		break;
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
2031da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
2041da177e4SLinus Torvalds 		chip->password = 0xe5;
2051da177e4SLinus Torvalds 		chip->pwd_reg = 3;
2061da177e4SLinus Torvalds 		break;
2071da177e4SLinus Torvalds #else	/* OPTi93X */
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930:
2101da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
2111da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
2121da177e4SLinus Torvalds 		chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d;
21310a3061aSOndrej Zary 		if (!chip->mc_indir_index)
2141da177e4SLinus Torvalds 			chip->mc_indir_index = 0xe0e;
2151da177e4SLinus Torvalds 		chip->password = 0xe4;
2161da177e4SLinus Torvalds 		chip->pwd_reg = 0;
2171da177e4SLinus Torvalds 		break;
2181da177e4SLinus Torvalds #endif	/* OPTi93X */
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds 	default:
2214c9f1d3eSTakashi Iwai 		snd_printk(KERN_ERR "chip %d not supported\n", hardware);
2221da177e4SLinus Torvalds 		return -ENODEV;
2231da177e4SLinus Torvalds 	}
2241da177e4SLinus Torvalds 	return 0;
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds 
227346c7a68STakashi Iwai static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,
2281da177e4SLinus Torvalds 				      unsigned char reg)
2291da177e4SLinus Torvalds {
2301da177e4SLinus Torvalds 	unsigned long flags;
2311da177e4SLinus Torvalds 	unsigned char retval = 0xff;
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
2341da177e4SLinus Torvalds 	outb(chip->password, chip->mc_base + chip->pwd_reg);
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds 	switch (chip->hardware) {
2371da177e4SLinus Torvalds #ifndef OPTi93X
2381da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
2391da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
2401da177e4SLinus Torvalds 		if (reg > 7) {
2411da177e4SLinus Torvalds 			outb(reg, chip->mc_base + 8);
2421da177e4SLinus Torvalds 			outb(chip->password, chip->mc_base + chip->pwd_reg);
2431da177e4SLinus Torvalds 			retval = inb(chip->mc_base + 9);
2441da177e4SLinus Torvalds 			break;
2451da177e4SLinus Torvalds 		}
246c0dbbdadSGustavo A. R. Silva 		fallthrough;
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
2491da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
2501da177e4SLinus Torvalds 		retval = inb(chip->mc_base + reg);
2511da177e4SLinus Torvalds 		break;
2521da177e4SLinus Torvalds #else	/* OPTi93X */
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930:
2551da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
2561da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
2571da177e4SLinus Torvalds 		outb(reg, chip->mc_indir_index);
2581da177e4SLinus Torvalds 		outb(chip->password, chip->mc_base + chip->pwd_reg);
2591da177e4SLinus Torvalds 		retval = inb(chip->mc_indir_index + 1);
2601da177e4SLinus Torvalds 		break;
2611da177e4SLinus Torvalds #endif	/* OPTi93X */
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 	default:
2644c9f1d3eSTakashi Iwai 		snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
2651da177e4SLinus Torvalds 	}
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
2681da177e4SLinus Torvalds 	return retval;
2691da177e4SLinus Torvalds }
2701da177e4SLinus Torvalds 
271346c7a68STakashi Iwai static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
2721da177e4SLinus Torvalds 			      unsigned char value)
2731da177e4SLinus Torvalds {
2741da177e4SLinus Torvalds 	unsigned long flags;
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
2771da177e4SLinus Torvalds 	outb(chip->password, chip->mc_base + chip->pwd_reg);
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds 	switch (chip->hardware) {
2801da177e4SLinus Torvalds #ifndef OPTi93X
2811da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
2821da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
2831da177e4SLinus Torvalds 		if (reg > 7) {
2841da177e4SLinus Torvalds 			outb(reg, chip->mc_base + 8);
2851da177e4SLinus Torvalds 			outb(chip->password, chip->mc_base + chip->pwd_reg);
2861da177e4SLinus Torvalds 			outb(value, chip->mc_base + 9);
2871da177e4SLinus Torvalds 			break;
2881da177e4SLinus Torvalds 		}
289c0dbbdadSGustavo A. R. Silva 		fallthrough;
2901da177e4SLinus Torvalds 
2911da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
2921da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
2931da177e4SLinus Torvalds 		outb(value, chip->mc_base + reg);
2941da177e4SLinus Torvalds 		break;
2951da177e4SLinus Torvalds #else	/* OPTi93X */
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930:
2981da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
2991da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
3001da177e4SLinus Torvalds 		outb(reg, chip->mc_indir_index);
3011da177e4SLinus Torvalds 		outb(chip->password, chip->mc_base + chip->pwd_reg);
3021da177e4SLinus Torvalds 		outb(value, chip->mc_indir_index + 1);
3031da177e4SLinus Torvalds 		break;
3041da177e4SLinus Torvalds #endif	/* OPTi93X */
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds 	default:
3074c9f1d3eSTakashi Iwai 		snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
3081da177e4SLinus Torvalds 	}
3091da177e4SLinus Torvalds 
3101da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
3111da177e4SLinus Torvalds }
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 
3145ce00760SArnd Bergmann static inline void snd_opti9xx_write_mask(struct snd_opti9xx *chip,
3155ce00760SArnd Bergmann 		unsigned char reg, unsigned char value, unsigned char mask)
3165ce00760SArnd Bergmann {
3175ce00760SArnd Bergmann 	unsigned char oldval = snd_opti9xx_read(chip, reg);
3181da177e4SLinus Torvalds 
3195ce00760SArnd Bergmann 	snd_opti9xx_write(chip, reg, (oldval & ~mask) | (value & mask));
3205ce00760SArnd Bergmann }
3211da177e4SLinus Torvalds 
32259b1f084STakashi Iwai static int snd_opti9xx_configure(struct snd_opti9xx *chip,
323fd8d4735SKrzysztof Helt 					   long port,
324d8ea2393SKrzysztof Helt 					   int irq, int dma1, int dma2,
325d8ea2393SKrzysztof Helt 					   long mpu_port, int mpu_irq)
3261da177e4SLinus Torvalds {
3271da177e4SLinus Torvalds 	unsigned char wss_base_bits;
3281da177e4SLinus Torvalds 	unsigned char irq_bits;
3291da177e4SLinus Torvalds 	unsigned char dma_bits;
3301da177e4SLinus Torvalds 	unsigned char mpu_port_bits = 0;
3311da177e4SLinus Torvalds 	unsigned char mpu_irq_bits;
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds 	switch (chip->hardware) {
3341da177e4SLinus Torvalds #ifndef OPTi93X
3351da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
336fd8d4735SKrzysztof Helt 		/* opti 929 mode (?), OPL3 clock output, audio enable */
3371da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc);
338fd8d4735SKrzysztof Helt 		/* enable wave audio */
3391da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
340c0dbbdadSGustavo A. R. Silva 		fallthrough;
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
343fd8d4735SKrzysztof Helt 		/* enable WSS mode */
3441da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
345fd8d4735SKrzysztof Helt 		/* OPL3 FM synthesis */
3461da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
347fd8d4735SKrzysztof Helt 		/* disable Sound Blaster IRQ and DMA */
3481da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
3491da177e4SLinus Torvalds #ifdef CS4231
350fd8d4735SKrzysztof Helt 		/* cs4231/4248 fix enabled */
3511da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
3521da177e4SLinus Torvalds #else
353fd8d4735SKrzysztof Helt 		/* cs4231/4248 fix disabled */
3541da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
3551da177e4SLinus Torvalds #endif	/* CS4231 */
3561da177e4SLinus Torvalds 		break;
3571da177e4SLinus Torvalds 
3581da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
3591da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
3601da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
3611da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
3621da177e4SLinus Torvalds 		/*
3631da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae);
3641da177e4SLinus Torvalds 		*/
3651da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
3661da177e4SLinus Torvalds #ifdef CS4231
3671da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
3681da177e4SLinus Torvalds #else
3691da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
3701da177e4SLinus Torvalds #endif	/* CS4231 */
3711da177e4SLinus Torvalds 		break;
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds #else	/* OPTi93X */
3741da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
37510a3061aSOndrej Zary 		/* disable 3D sound (set GPIO1 as output, low) */
37610a3061aSOndrej Zary 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(20), 0x04, 0x0c);
377c0dbbdadSGustavo A. R. Silva 		fallthrough;
378c0dbbdadSGustavo A. R. Silva 
37974ce5a46STakashi Iwai 	case OPTi9XX_HW_82C933:
380f81b953dSKrzysztof Helt 		/*
381f81b953dSKrzysztof Helt 		 * The BTC 1817DW has QS1000 wavetable which is connected
382f81b953dSKrzysztof Helt 		 * to the serial digital input of the OPTI931.
383f81b953dSKrzysztof Helt 		 */
384f81b953dSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(21), 0x82, 0xff);
385f81b953dSKrzysztof Helt 		/*
386f81b953dSKrzysztof Helt 		 * This bit sets OPTI931 to automaticaly select FM
387f81b953dSKrzysztof Helt 		 * or digital input signal.
388f81b953dSKrzysztof Helt 		 */
389f81b953dSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01);
390c0dbbdadSGustavo A. R. Silva 		fallthrough;
391c0dbbdadSGustavo A. R. Silva 
39274ce5a46STakashi Iwai 	case OPTi9XX_HW_82C930:
3933ae5f36aSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
3943ae5f36aSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
3953ae5f36aSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
3963ae5f36aSKrzysztof Helt 			(chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
3973ae5f36aSKrzysztof Helt 			0x34);
3983ae5f36aSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
3991da177e4SLinus Torvalds 		break;
4001da177e4SLinus Torvalds #endif	/* OPTi93X */
4011da177e4SLinus Torvalds 
4021da177e4SLinus Torvalds 	default:
4034c9f1d3eSTakashi Iwai 		snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
4041da177e4SLinus Torvalds 		return -EINVAL;
4051da177e4SLinus Torvalds 	}
4061da177e4SLinus Torvalds 
407fd8d4735SKrzysztof Helt 	/* PnP resource says it decodes only 10 bits of address */
408fd8d4735SKrzysztof Helt 	switch (port & 0x3ff) {
409fd8d4735SKrzysztof Helt 	case 0x130:
410fd8d4735SKrzysztof Helt 		chip->wss_base = 0x530;
4111da177e4SLinus Torvalds 		wss_base_bits = 0x00;
4121da177e4SLinus Torvalds 		break;
413fd8d4735SKrzysztof Helt 	case 0x204:
414fd8d4735SKrzysztof Helt 		chip->wss_base = 0x604;
4151da177e4SLinus Torvalds 		wss_base_bits = 0x03;
4161da177e4SLinus Torvalds 		break;
417fd8d4735SKrzysztof Helt 	case 0x280:
418fd8d4735SKrzysztof Helt 		chip->wss_base = 0xe80;
4191da177e4SLinus Torvalds 		wss_base_bits = 0x01;
4201da177e4SLinus Torvalds 		break;
421fd8d4735SKrzysztof Helt 	case 0x340:
422fd8d4735SKrzysztof Helt 		chip->wss_base = 0xf40;
4231da177e4SLinus Torvalds 		wss_base_bits = 0x02;
4241da177e4SLinus Torvalds 		break;
4251da177e4SLinus Torvalds 	default:
426fd8d4735SKrzysztof Helt 		snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", port);
4271da177e4SLinus Torvalds 		goto __skip_base;
4281da177e4SLinus Torvalds 	}
4291da177e4SLinus Torvalds 	snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds __skip_base:
432d8ea2393SKrzysztof Helt 	switch (irq) {
4331da177e4SLinus Torvalds //#ifdef OPTi93X
4341da177e4SLinus Torvalds 	case 5:
4351da177e4SLinus Torvalds 		irq_bits = 0x05;
4361da177e4SLinus Torvalds 		break;
4371da177e4SLinus Torvalds //#endif	/* OPTi93X */
4381da177e4SLinus Torvalds 	case 7:
4391da177e4SLinus Torvalds 		irq_bits = 0x01;
4401da177e4SLinus Torvalds 		break;
4411da177e4SLinus Torvalds 	case 9:
4421da177e4SLinus Torvalds 		irq_bits = 0x02;
4431da177e4SLinus Torvalds 		break;
4441da177e4SLinus Torvalds 	case 10:
4451da177e4SLinus Torvalds 		irq_bits = 0x03;
4461da177e4SLinus Torvalds 		break;
4471da177e4SLinus Torvalds 	case 11:
4481da177e4SLinus Torvalds 		irq_bits = 0x04;
4491da177e4SLinus Torvalds 		break;
4501da177e4SLinus Torvalds 	default:
451d8ea2393SKrzysztof Helt 		snd_printk(KERN_WARNING "WSS irq # %d not valid\n", irq);
4521da177e4SLinus Torvalds 		goto __skip_resources;
4531da177e4SLinus Torvalds 	}
4541da177e4SLinus Torvalds 
455d8ea2393SKrzysztof Helt 	switch (dma1) {
4561da177e4SLinus Torvalds 	case 0:
4571da177e4SLinus Torvalds 		dma_bits = 0x01;
4581da177e4SLinus Torvalds 		break;
4591da177e4SLinus Torvalds 	case 1:
4601da177e4SLinus Torvalds 		dma_bits = 0x02;
4611da177e4SLinus Torvalds 		break;
4621da177e4SLinus Torvalds 	case 3:
4631da177e4SLinus Torvalds 		dma_bits = 0x03;
4641da177e4SLinus Torvalds 		break;
4651da177e4SLinus Torvalds 	default:
466d8ea2393SKrzysztof Helt 		snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n", dma1);
4671da177e4SLinus Torvalds 		goto __skip_resources;
4681da177e4SLinus Torvalds 	}
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
471d8ea2393SKrzysztof Helt 	if (dma1 == dma2) {
4724c9f1d3eSTakashi Iwai 		snd_printk(KERN_ERR "don't want to share dmas\n");
4731da177e4SLinus Torvalds 		return -EBUSY;
4741da177e4SLinus Torvalds 	}
4751da177e4SLinus Torvalds 
476d8ea2393SKrzysztof Helt 	switch (dma2) {
4771da177e4SLinus Torvalds 	case 0:
4781da177e4SLinus Torvalds 	case 1:
4791da177e4SLinus Torvalds 		break;
4801da177e4SLinus Torvalds 	default:
481d8ea2393SKrzysztof Helt 		snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n", dma2);
4821da177e4SLinus Torvalds 		goto __skip_resources;
4831da177e4SLinus Torvalds 	}
4841da177e4SLinus Torvalds 	dma_bits |= 0x04;
4851da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
4861da177e4SLinus Torvalds 
4871da177e4SLinus Torvalds #ifndef OPTi93X
488fd8d4735SKrzysztof Helt 	 outb(irq_bits << 3 | dma_bits, chip->wss_base);
4891da177e4SLinus Torvalds #else /* OPTi93X */
4901da177e4SLinus Torvalds 	snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));
4911da177e4SLinus Torvalds #endif /* OPTi93X */
4921da177e4SLinus Torvalds 
4931da177e4SLinus Torvalds __skip_resources:
4941da177e4SLinus Torvalds 	if (chip->hardware > OPTi9XX_HW_82C928) {
495d8ea2393SKrzysztof Helt 		switch (mpu_port) {
4961da177e4SLinus Torvalds 		case 0:
4971da177e4SLinus Torvalds 		case -1:
4981da177e4SLinus Torvalds 			break;
4991da177e4SLinus Torvalds 		case 0x300:
5001da177e4SLinus Torvalds 			mpu_port_bits = 0x03;
5011da177e4SLinus Torvalds 			break;
5021da177e4SLinus Torvalds 		case 0x310:
5031da177e4SLinus Torvalds 			mpu_port_bits = 0x02;
5041da177e4SLinus Torvalds 			break;
5051da177e4SLinus Torvalds 		case 0x320:
5061da177e4SLinus Torvalds 			mpu_port_bits = 0x01;
5071da177e4SLinus Torvalds 			break;
5081da177e4SLinus Torvalds 		case 0x330:
5091da177e4SLinus Torvalds 			mpu_port_bits = 0x00;
5101da177e4SLinus Torvalds 			break;
5111da177e4SLinus Torvalds 		default:
5124c9f1d3eSTakashi Iwai 			snd_printk(KERN_WARNING
513d8ea2393SKrzysztof Helt 				   "MPU-401 port 0x%lx not valid\n", mpu_port);
5141da177e4SLinus Torvalds 			goto __skip_mpu;
5151da177e4SLinus Torvalds 		}
5161da177e4SLinus Torvalds 
517d8ea2393SKrzysztof Helt 		switch (mpu_irq) {
5181da177e4SLinus Torvalds 		case 5:
5191da177e4SLinus Torvalds 			mpu_irq_bits = 0x02;
5201da177e4SLinus Torvalds 			break;
5211da177e4SLinus Torvalds 		case 7:
5221da177e4SLinus Torvalds 			mpu_irq_bits = 0x03;
5231da177e4SLinus Torvalds 			break;
5241da177e4SLinus Torvalds 		case 9:
5251da177e4SLinus Torvalds 			mpu_irq_bits = 0x00;
5261da177e4SLinus Torvalds 			break;
5271da177e4SLinus Torvalds 		case 10:
5281da177e4SLinus Torvalds 			mpu_irq_bits = 0x01;
5291da177e4SLinus Torvalds 			break;
5301da177e4SLinus Torvalds 		default:
5314c9f1d3eSTakashi Iwai 			snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n",
532d8ea2393SKrzysztof Helt 				mpu_irq);
5331da177e4SLinus Torvalds 			goto __skip_mpu;
5341da177e4SLinus Torvalds 		}
5351da177e4SLinus Torvalds 
5361da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6),
537d8ea2393SKrzysztof Helt 			(mpu_port <= 0) ? 0x00 :
5381da177e4SLinus Torvalds 				0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
5391da177e4SLinus Torvalds 			0xf8);
5401da177e4SLinus Torvalds 	}
5411da177e4SLinus Torvalds __skip_mpu:
5421da177e4SLinus Torvalds 
5431da177e4SLinus Torvalds 	return 0;
5441da177e4SLinus Torvalds }
5451da177e4SLinus Torvalds 
5461da177e4SLinus Torvalds #ifdef OPTi93X
5471da177e4SLinus Torvalds 
548e9d0a803SKrzysztof Helt static const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_step, -9300, 300, 0);
549e9d0a803SKrzysztof Helt static const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
550e9d0a803SKrzysztof Helt static const DECLARE_TLV_DB_SCALE(db_scale_4bit_12db_max, -3300, 300, 0);
551b2e8d7daSKrzysztof Helt 
552fdd1f6fdSTakashi Iwai static const struct snd_kcontrol_new snd_opti93x_controls[] = {
553b2e8d7daSKrzysztof Helt WSS_DOUBLE("Master Playback Switch", 0,
554b2e8d7daSKrzysztof Helt 		OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
555b2e8d7daSKrzysztof Helt WSS_DOUBLE_TLV("Master Playback Volume", 0,
556b2e8d7daSKrzysztof Helt 		OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1,
557e9d0a803SKrzysztof Helt 		db_scale_5bit_3db_step),
558e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("PCM Playback Volume", 0,
559e9d0a803SKrzysztof Helt 		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1,
560e9d0a803SKrzysztof Helt 		db_scale_5bit),
561e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("FM Playback Volume", 0,
562e9d0a803SKrzysztof Helt 		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1,
563e9d0a803SKrzysztof Helt 		db_scale_4bit_12db_max),
564b2e8d7daSKrzysztof Helt WSS_DOUBLE("Line Playback Switch", 0,
565b2e8d7daSKrzysztof Helt 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
566e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("Line Playback Volume", 0,
567e9d0a803SKrzysztof Helt 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1,
568e9d0a803SKrzysztof Helt 		db_scale_4bit_12db_max),
569b2e8d7daSKrzysztof Helt WSS_DOUBLE("Mic Playback Switch", 0,
570b2e8d7daSKrzysztof Helt 		OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
571e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("Mic Playback Volume", 0,
572e9d0a803SKrzysztof Helt 		OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1,
573e9d0a803SKrzysztof Helt 		db_scale_4bit_12db_max),
574e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("CD Playback Volume", 0,
575e9d0a803SKrzysztof Helt 		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1,
576e9d0a803SKrzysztof Helt 		db_scale_4bit_12db_max),
577b2e8d7daSKrzysztof Helt WSS_DOUBLE("Aux Playback Switch", 0,
578b2e8d7daSKrzysztof Helt 		OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
579e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("Aux Playback Volume", 0,
580e9d0a803SKrzysztof Helt 		OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1,
581e9d0a803SKrzysztof Helt 		db_scale_4bit_12db_max),
582b2e8d7daSKrzysztof Helt };
583b2e8d7daSKrzysztof Helt 
5841bff292eSBill Pemberton static int snd_opti93x_mixer(struct snd_wss *chip)
585b2e8d7daSKrzysztof Helt {
586b2e8d7daSKrzysztof Helt 	struct snd_card *card;
587b2e8d7daSKrzysztof Helt 	unsigned int idx;
588b2e8d7daSKrzysztof Helt 	struct snd_ctl_elem_id id1, id2;
589b2e8d7daSKrzysztof Helt 	int err;
590b2e8d7daSKrzysztof Helt 
591b2e8d7daSKrzysztof Helt 	if (snd_BUG_ON(!chip || !chip->pcm))
592b2e8d7daSKrzysztof Helt 		return -EINVAL;
593b2e8d7daSKrzysztof Helt 
594b2e8d7daSKrzysztof Helt 	card = chip->card;
595b2e8d7daSKrzysztof Helt 
596b2e8d7daSKrzysztof Helt 	strcpy(card->mixername, chip->pcm->name);
597b2e8d7daSKrzysztof Helt 
598b2e8d7daSKrzysztof Helt 	memset(&id1, 0, sizeof(id1));
599b2e8d7daSKrzysztof Helt 	memset(&id2, 0, sizeof(id2));
600b2e8d7daSKrzysztof Helt 	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
601b2e8d7daSKrzysztof Helt 	/* reassign AUX0 switch to CD */
602b2e8d7daSKrzysztof Helt 	strcpy(id1.name, "Aux Playback Switch");
603b2e8d7daSKrzysztof Helt 	strcpy(id2.name, "CD Playback Switch");
604b2e8d7daSKrzysztof Helt 	err = snd_ctl_rename_id(card, &id1, &id2);
605b2e8d7daSKrzysztof Helt 	if (err < 0) {
606b2e8d7daSKrzysztof Helt 		snd_printk(KERN_ERR "Cannot rename opti93x control\n");
607b2e8d7daSKrzysztof Helt 		return err;
608b2e8d7daSKrzysztof Helt 	}
609b2e8d7daSKrzysztof Helt 	/* reassign AUX1 switch to FM */
610b2e8d7daSKrzysztof Helt 	strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
611b2e8d7daSKrzysztof Helt 	strcpy(id2.name, "FM Playback Switch");
612b2e8d7daSKrzysztof Helt 	err = snd_ctl_rename_id(card, &id1, &id2);
613b2e8d7daSKrzysztof Helt 	if (err < 0) {
614b2e8d7daSKrzysztof Helt 		snd_printk(KERN_ERR "Cannot rename opti93x control\n");
615b2e8d7daSKrzysztof Helt 		return err;
616b2e8d7daSKrzysztof Helt 	}
617b2e8d7daSKrzysztof Helt 	/* remove AUX1 volume */
618b2e8d7daSKrzysztof Helt 	strcpy(id1.name, "Aux Playback Volume"); id1.index = 1;
619b2e8d7daSKrzysztof Helt 	snd_ctl_remove_id(card, &id1);
620b2e8d7daSKrzysztof Helt 
621b2e8d7daSKrzysztof Helt 	/* Replace WSS volume controls with OPTi93x volume controls */
622b2e8d7daSKrzysztof Helt 	id1.index = 0;
623b2e8d7daSKrzysztof Helt 	for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
624b2e8d7daSKrzysztof Helt 		strcpy(id1.name, snd_opti93x_controls[idx].name);
625b2e8d7daSKrzysztof Helt 		snd_ctl_remove_id(card, &id1);
626b2e8d7daSKrzysztof Helt 
627b2e8d7daSKrzysztof Helt 		err = snd_ctl_add(card,
628b2e8d7daSKrzysztof Helt 				snd_ctl_new1(&snd_opti93x_controls[idx], chip));
629b2e8d7daSKrzysztof Helt 		if (err < 0)
630b2e8d7daSKrzysztof Helt 			return err;
631b2e8d7daSKrzysztof Helt 	}
632b2e8d7daSKrzysztof Helt 	return 0;
633b2e8d7daSKrzysztof Helt }
634b2e8d7daSKrzysztof Helt 
6357d12e780SDavid Howells static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
6361da177e4SLinus Torvalds {
6375f60e496SKrzysztof Helt 	struct snd_opti9xx *chip = dev_id;
6385f60e496SKrzysztof Helt 	struct snd_wss *codec = chip->codec;
6391da177e4SLinus Torvalds 	unsigned char status;
6401da177e4SLinus Torvalds 
6415f60e496SKrzysztof Helt 	if (!codec)
6425f60e496SKrzysztof Helt 		return IRQ_HANDLED;
6435f60e496SKrzysztof Helt 
6449f240a55SKrzysztof Helt 	status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11));
6451da177e4SLinus Torvalds 	if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)
6461da177e4SLinus Torvalds 		snd_pcm_period_elapsed(codec->playback_substream);
6471da177e4SLinus Torvalds 	if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) {
6487779f75fSKrzysztof Helt 		snd_wss_overrange(codec);
6491da177e4SLinus Torvalds 		snd_pcm_period_elapsed(codec->capture_substream);
6501da177e4SLinus Torvalds 	}
6511da177e4SLinus Torvalds 	outb(0x00, OPTi93X_PORT(codec, STATUS));
6521da177e4SLinus Torvalds 	return IRQ_HANDLED;
6531da177e4SLinus Torvalds }
6541da177e4SLinus Torvalds 
6551da177e4SLinus Torvalds #endif /* OPTi93X */
6561da177e4SLinus Torvalds 
6572973ee4aSTakashi Iwai static int snd_opti9xx_read_check(struct snd_card *card,
6582973ee4aSTakashi Iwai 				  struct snd_opti9xx *chip)
659e6960e19SKrzysztof Helt {
660e6960e19SKrzysztof Helt 	unsigned char value;
661e6960e19SKrzysztof Helt #ifdef OPTi93X
662e6960e19SKrzysztof Helt 	unsigned long flags;
663e6960e19SKrzysztof Helt #endif
664e6960e19SKrzysztof Helt 
6652973ee4aSTakashi Iwai 	chip->res_mc_base =
6662973ee4aSTakashi Iwai 		devm_request_region(card->dev, chip->mc_base,
6672973ee4aSTakashi Iwai 				    chip->mc_base_size, "OPTi9xx MC");
6682973ee4aSTakashi Iwai 	if (!chip->res_mc_base)
669e6960e19SKrzysztof Helt 		return -EBUSY;
670e6960e19SKrzysztof Helt #ifndef OPTi93X
671e6960e19SKrzysztof Helt 	value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
672e6960e19SKrzysztof Helt 	if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1)))
673e6960e19SKrzysztof Helt 		if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
674e6960e19SKrzysztof Helt 			return 0;
675e6960e19SKrzysztof Helt #else	/* OPTi93X */
6762973ee4aSTakashi Iwai 	chip->res_mc_indir =
6772973ee4aSTakashi Iwai 		devm_request_region(card->dev, chip->mc_indir_index, 2,
678e6960e19SKrzysztof Helt 				    "OPTi93x MC");
6792973ee4aSTakashi Iwai 	if (!chip->res_mc_indir)
680e6960e19SKrzysztof Helt 		return -EBUSY;
681e6960e19SKrzysztof Helt 
682e6960e19SKrzysztof Helt 	spin_lock_irqsave(&chip->lock, flags);
683e6960e19SKrzysztof Helt 	outb(chip->password, chip->mc_base + chip->pwd_reg);
684e6960e19SKrzysztof Helt 	outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base);
685e6960e19SKrzysztof Helt 	spin_unlock_irqrestore(&chip->lock, flags);
686e6960e19SKrzysztof Helt 
687e6960e19SKrzysztof Helt 	value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));
688e6960e19SKrzysztof Helt 	snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);
689e6960e19SKrzysztof Helt 	if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)
690e6960e19SKrzysztof Helt 		return 0;
691e6960e19SKrzysztof Helt 
6922973ee4aSTakashi Iwai 	devm_release_resource(card->dev, chip->res_mc_indir);
693e6960e19SKrzysztof Helt 	chip->res_mc_indir = NULL;
694e6960e19SKrzysztof Helt #endif	/* OPTi93X */
6952973ee4aSTakashi Iwai 	devm_release_resource(card->dev, chip->res_mc_base);
696e6960e19SKrzysztof Helt 	chip->res_mc_base = NULL;
697e6960e19SKrzysztof Helt 
698e6960e19SKrzysztof Helt 	return -ENODEV;
699e6960e19SKrzysztof Helt }
700e6960e19SKrzysztof Helt 
7011bff292eSBill Pemberton static int snd_card_opti9xx_detect(struct snd_card *card,
7025e24c1c1STakashi Iwai 				   struct snd_opti9xx *chip)
7031da177e4SLinus Torvalds {
7041da177e4SLinus Torvalds 	int i, err;
7051da177e4SLinus Torvalds 
7061da177e4SLinus Torvalds #ifndef OPTi93X
7071da177e4SLinus Torvalds 	for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {
708e6960e19SKrzysztof Helt #else
7091da177e4SLinus Torvalds 	for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {
710e6960e19SKrzysztof Helt #endif
711e6960e19SKrzysztof Helt 		err = snd_opti9xx_init(chip, i);
712e6960e19SKrzysztof Helt 		if (err < 0)
7131da177e4SLinus Torvalds 			return err;
7141da177e4SLinus Torvalds 
7152973ee4aSTakashi Iwai 		err = snd_opti9xx_read_check(card, chip);
716e6960e19SKrzysztof Helt 		if (err == 0)
7171da177e4SLinus Torvalds 			return 1;
718e6960e19SKrzysztof Helt #ifdef OPTi93X
719e6960e19SKrzysztof Helt 		chip->mc_indir_index = 0;
720e6960e19SKrzysztof Helt #endif
7211da177e4SLinus Torvalds 	}
7221da177e4SLinus Torvalds 	return -ENODEV;
7231da177e4SLinus Torvalds }
7241da177e4SLinus Torvalds 
7251da177e4SLinus Torvalds #ifdef CONFIG_PNP
7261bff292eSBill Pemberton static int snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
7275e24c1c1STakashi Iwai 				struct pnp_card_link *card,
7281da177e4SLinus Torvalds 				const struct pnp_card_device_id *pid)
7291da177e4SLinus Torvalds {
7301da177e4SLinus Torvalds 	struct pnp_dev *pdev;
7311da177e4SLinus Torvalds 	int err;
732fd8d4735SKrzysztof Helt 	struct pnp_dev *devmpu;
733fd8d4735SKrzysztof Helt #ifndef OPTi93X
734fd8d4735SKrzysztof Helt 	struct pnp_dev *devmc;
735fd8d4735SKrzysztof Helt #endif
7361da177e4SLinus Torvalds 
737fd8d4735SKrzysztof Helt 	pdev = pnp_request_card_device(card, pid->devs[0].id, NULL);
738fd8d4735SKrzysztof Helt 	if (pdev == NULL)
7391da177e4SLinus Torvalds 		return -EBUSY;
740109c53f8SRene Herman 
7411da177e4SLinus Torvalds 	err = pnp_activate_dev(pdev);
7421da177e4SLinus Torvalds 	if (err < 0) {
7431da177e4SLinus Torvalds 		snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
7441da177e4SLinus Torvalds 		return err;
7451da177e4SLinus Torvalds 	}
7461da177e4SLinus Torvalds 
7471da177e4SLinus Torvalds #ifdef OPTi93X
7481da177e4SLinus Torvalds 	port = pnp_port_start(pdev, 0) - 4;
7491ea73412STakashi Iwai 	fm_port = pnp_port_start(pdev, 1) + 8;
75010a3061aSOndrej Zary 	/* adjust mc_indir_index - some cards report it at 0xe?d,
75110a3061aSOndrej Zary 	   other at 0xe?c but it really is always at 0xe?e */
75210a3061aSOndrej Zary 	chip->mc_indir_index = (pnp_port_start(pdev, 3) & ~0xf) | 0xe;
7531da177e4SLinus Torvalds #else
754fd8d4735SKrzysztof Helt 	devmc = pnp_request_card_device(card, pid->devs[2].id, NULL);
755fd8d4735SKrzysztof Helt 	if (devmc == NULL)
756fd8d4735SKrzysztof Helt 		return -EBUSY;
757fd8d4735SKrzysztof Helt 
758fd8d4735SKrzysztof Helt 	err = pnp_activate_dev(devmc);
759fd8d4735SKrzysztof Helt 	if (err < 0) {
760fd8d4735SKrzysztof Helt 		snd_printk(KERN_ERR "MC pnp configure failure: %d\n", err);
761fd8d4735SKrzysztof Helt 		return err;
762fd8d4735SKrzysztof Helt 	}
763fd8d4735SKrzysztof Helt 
7641da177e4SLinus Torvalds 	port = pnp_port_start(pdev, 1);
7651ea73412STakashi Iwai 	fm_port = pnp_port_start(pdev, 2) + 8;
766fd8d4735SKrzysztof Helt 	/*
767fd8d4735SKrzysztof Helt 	 * The MC(0) is never accessed and card does not
768fd8d4735SKrzysztof Helt 	 * include it in the PnP resource range. OPTI93x include it.
769fd8d4735SKrzysztof Helt 	 */
770fd8d4735SKrzysztof Helt 	chip->mc_base = pnp_port_start(devmc, 0) - 1;
771fd8d4735SKrzysztof Helt 	chip->mc_base_size = pnp_port_len(devmc, 0) + 1;
7721da177e4SLinus Torvalds #endif	/* OPTi93X */
7731da177e4SLinus Torvalds 	irq = pnp_irq(pdev, 0);
7741da177e4SLinus Torvalds 	dma1 = pnp_dma(pdev, 0);
7751da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
7761da177e4SLinus Torvalds 	dma2 = pnp_dma(pdev, 1);
7771da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
7781da177e4SLinus Torvalds 
779fd8d4735SKrzysztof Helt 	devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
780fd8d4735SKrzysztof Helt 
781fd8d4735SKrzysztof Helt 	if (devmpu && mpu_port > 0) {
782fd8d4735SKrzysztof Helt 		err = pnp_activate_dev(devmpu);
7831da177e4SLinus Torvalds 		if (err < 0) {
784fd8d4735SKrzysztof Helt 			snd_printk(KERN_ERR "MPU401 pnp configure failure\n");
7851da177e4SLinus Torvalds 			mpu_port = -1;
7861da177e4SLinus Torvalds 		} else {
787fd8d4735SKrzysztof Helt 			mpu_port = pnp_port_start(devmpu, 0);
788fd8d4735SKrzysztof Helt 			mpu_irq = pnp_irq(devmpu, 0);
7891da177e4SLinus Torvalds 		}
7901da177e4SLinus Torvalds 	}
7911da177e4SLinus Torvalds 	return pid->driver_data;
7921da177e4SLinus Torvalds }
7931da177e4SLinus Torvalds #endif	/* CONFIG_PNP */
7941da177e4SLinus Torvalds 
7951bff292eSBill Pemberton static int snd_opti9xx_probe(struct snd_card *card)
7961da177e4SLinus Torvalds {
79780e0a7c0STakashi Iwai 	static const long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
7981da177e4SLinus Torvalds 	int error;
799d8ea2393SKrzysztof Helt 	int xdma2;
80099a0b768STakashi Iwai 	struct snd_opti9xx *chip = card->private_data;
8017779f75fSKrzysztof Helt 	struct snd_wss *codec;
802346c7a68STakashi Iwai 	struct snd_rawmidi *rmidi;
803346c7a68STakashi Iwai 	struct snd_hwdep *synth;
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
806d8ea2393SKrzysztof Helt 	xdma2 = dma2;
807a0d9274cSRene Herman #else
808d8ea2393SKrzysztof Helt 	xdma2 = -1;
8091da177e4SLinus Torvalds #endif
8101da177e4SLinus Torvalds 
811d8ea2393SKrzysztof Helt 	if (port == SNDRV_AUTO_PORT) {
812d8ea2393SKrzysztof Helt 		port = snd_legacy_find_free_ioport(possible_ports, 4);
813d8ea2393SKrzysztof Helt 		if (port < 0) {
8144c9f1d3eSTakashi Iwai 			snd_printk(KERN_ERR "unable to find a free WSS port\n");
8151da177e4SLinus Torvalds 			return -EBUSY;
8161da177e4SLinus Torvalds 		}
8171da177e4SLinus Torvalds 	}
818d8ea2393SKrzysztof Helt 	error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2,
819d8ea2393SKrzysztof Helt 				      mpu_port, mpu_irq);
8207779f75fSKrzysztof Helt 	if (error)
8211da177e4SLinus Torvalds 		return error;
8221da177e4SLinus Torvalds 
823fd8d4735SKrzysztof Helt 	error = snd_wss_create(card, chip->wss_base + 4, -1, irq, dma1, xdma2,
824a0d9274cSRene Herman #ifdef OPTi93X
8257779f75fSKrzysztof Helt 			       WSS_HW_OPTI93X, WSS_HWSHARE_IRQ,
826a0d9274cSRene Herman #else
827a0d9274cSRene Herman 			       WSS_HW_DETECT, 0,
8289f240a55SKrzysztof Helt #endif
8297779f75fSKrzysztof Helt 			       &codec);
8307779f75fSKrzysztof Helt 	if (error < 0)
8311da177e4SLinus Torvalds 		return error;
8329f240a55SKrzysztof Helt 	chip->codec = codec;
833fa60c065SLars-Peter Clausen 	error = snd_wss_pcm(codec, 0);
8345664daa1SKrzysztof Helt 	if (error < 0)
8355664daa1SKrzysztof Helt 		return error;
8367779f75fSKrzysztof Helt 	error = snd_wss_mixer(codec);
8377779f75fSKrzysztof Helt 	if (error < 0)
8381da177e4SLinus Torvalds 		return error;
839b2e8d7daSKrzysztof Helt #ifdef OPTi93X
840b2e8d7daSKrzysztof Helt 	error = snd_opti93x_mixer(codec);
841b2e8d7daSKrzysztof Helt 	if (error < 0)
842b2e8d7daSKrzysztof Helt 		return error;
843b2e8d7daSKrzysztof Helt #endif
8449f240a55SKrzysztof Helt #ifdef CS4231
845fa60c065SLars-Peter Clausen 	error = snd_wss_timer(codec, 0);
8467779f75fSKrzysztof Helt 	if (error < 0)
8471da177e4SLinus Torvalds 		return error;
8485664daa1SKrzysztof Helt #endif
8495664daa1SKrzysztof Helt #ifdef OPTi93X
8502973ee4aSTakashi Iwai 	error = devm_request_irq(card->dev, irq, snd_opti93x_interrupt,
85188e24c3aSYong Zhang 				 0, DEV_NAME" - WSS", chip);
8529f240a55SKrzysztof Helt 	if (error < 0) {
8535f60e496SKrzysztof Helt 		snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", irq);
8549f240a55SKrzysztof Helt 		return error;
8559f240a55SKrzysztof Helt 	}
8569f240a55SKrzysztof Helt #endif
857d8ea2393SKrzysztof Helt 	chip->irq = irq;
85816d9fb1dSTakashi Iwai 	card->sync_irq = chip->irq;
8591da177e4SLinus Torvalds 	strcpy(card->driver, chip->name);
8601da177e4SLinus Torvalds 	sprintf(card->shortname, "OPTi %s", card->driver);
8611da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
86224d22077SArnd Bergmann 	snprintf(card->longname, sizeof(card->longname),
86324d22077SArnd Bergmann 		 "%s, %s at 0x%lx, irq %d, dma %d&%d",
864fa60c065SLars-Peter Clausen 		 card->shortname, codec->pcm->name,
865fd8d4735SKrzysztof Helt 		 chip->wss_base + 4, irq, dma1, xdma2);
8661da177e4SLinus Torvalds #else
86724d22077SArnd Bergmann 	snprintf(card->longname, sizeof(card->longname),
86824d22077SArnd Bergmann 		 "%s, %s at 0x%lx, irq %d, dma %d",
869fa60c065SLars-Peter Clausen 		 card->shortname, codec->pcm->name, chip->wss_base + 4, irq,
870fa60c065SLars-Peter Clausen 		 dma1);
8711da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
8721da177e4SLinus Torvalds 
873d8ea2393SKrzysztof Helt 	if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
8741da177e4SLinus Torvalds 		rmidi = NULL;
875d8ea2393SKrzysztof Helt 	else {
876d8ea2393SKrzysztof Helt 		error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
877dba8b469SClemens Ladisch 				mpu_port, 0, mpu_irq, &rmidi);
878d8ea2393SKrzysztof Helt 		if (error)
87999a0b768STakashi Iwai 			snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
880d8ea2393SKrzysztof Helt 				   mpu_port);
881d8ea2393SKrzysztof Helt 	}
8821da177e4SLinus Torvalds 
883d8ea2393SKrzysztof Helt 	if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
884346c7a68STakashi Iwai 		struct snd_opl3 *opl3 = NULL;
8851da177e4SLinus Torvalds #ifndef OPTi93X
8861da177e4SLinus Torvalds 		if (chip->hardware == OPTi9XX_HW_82C928 ||
8871da177e4SLinus Torvalds 		    chip->hardware == OPTi9XX_HW_82C929 ||
8881da177e4SLinus Torvalds 		    chip->hardware == OPTi9XX_HW_82C924) {
889346c7a68STakashi Iwai 			struct snd_opl4 *opl4;
8901da177e4SLinus Torvalds 			/* assume we have an OPL4 */
8911da177e4SLinus Torvalds 			snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
8921da177e4SLinus Torvalds 					       0x20, 0x20);
893d8ea2393SKrzysztof Helt 			if (snd_opl4_create(card, fm_port, fm_port - 8,
8941da177e4SLinus Torvalds 					    2, &opl3, &opl4) < 0) {
8951da177e4SLinus Torvalds 				/* no luck, use OPL3 instead */
8961da177e4SLinus Torvalds 				snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
8971da177e4SLinus Torvalds 						       0x00, 0x20);
8981da177e4SLinus Torvalds 			}
8991da177e4SLinus Torvalds 		}
9001da177e4SLinus Torvalds #endif	/* !OPTi93X */
901d8ea2393SKrzysztof Helt 		if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2,
9021da177e4SLinus Torvalds 					     OPL3_HW_AUTO, 0, &opl3) < 0) {
90399a0b768STakashi Iwai 			snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
904d8ea2393SKrzysztof Helt 				   fm_port, fm_port + 4 - 1);
9051da177e4SLinus Torvalds 		}
9061da177e4SLinus Torvalds 		if (opl3) {
907aa9c293aSKrzysztof Helt 			error = snd_opl3_hwdep_new(opl3, 0, 1, &synth);
908aa9c293aSKrzysztof Helt 			if (error < 0)
9091da177e4SLinus Torvalds 				return error;
9101da177e4SLinus Torvalds 		}
9111da177e4SLinus Torvalds 	}
9121da177e4SLinus Torvalds 
91399a0b768STakashi Iwai 	return snd_card_register(card);
91499a0b768STakashi Iwai }
91599a0b768STakashi Iwai 
9164323cc4dSTakashi Iwai static int snd_opti9xx_card_new(struct device *pdev, struct snd_card **cardp)
91799a0b768STakashi Iwai {
91899a0b768STakashi Iwai 	struct snd_card *card;
919b1a0aac0STakashi Iwai 	int err;
92099a0b768STakashi Iwai 
9212973ee4aSTakashi Iwai 	err = snd_devm_card_new(pdev, index, id, THIS_MODULE,
922c95eadd2STakashi Iwai 				sizeof(struct snd_opti9xx), &card);
923c95eadd2STakashi Iwai 	if (err < 0)
9243e7fb9f7STakashi Iwai 		return err;
9253e7fb9f7STakashi Iwai 	*cardp = card;
9263e7fb9f7STakashi Iwai 	return 0;
92799a0b768STakashi Iwai }
92899a0b768STakashi Iwai 
9291bff292eSBill Pemberton static int snd_opti9xx_isa_match(struct device *devptr,
9305e24c1c1STakashi Iwai 				 unsigned int dev)
9315e24c1c1STakashi Iwai {
932101f6f4bSTakashi Iwai #ifdef CONFIG_PNP
9335e24c1c1STakashi Iwai 	if (snd_opti9xx_pnp_is_probed)
9345e24c1c1STakashi Iwai 		return 0;
9355e24c1c1STakashi Iwai 	if (isapnp)
9365e24c1c1STakashi Iwai 		return 0;
937101f6f4bSTakashi Iwai #endif
9385e24c1c1STakashi Iwai 	return 1;
9395e24c1c1STakashi Iwai }
9405e24c1c1STakashi Iwai 
9411bff292eSBill Pemberton static int snd_opti9xx_isa_probe(struct device *devptr,
9425e24c1c1STakashi Iwai 				 unsigned int dev)
94399a0b768STakashi Iwai {
94499a0b768STakashi Iwai 	struct snd_card *card;
94599a0b768STakashi Iwai 	int error;
94680e0a7c0STakashi Iwai 	static const long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1};
94799a0b768STakashi Iwai #ifdef OPTi93X
94880e0a7c0STakashi Iwai 	static const int possible_irqs[] = {5, 9, 10, 11, 7, -1};
94999a0b768STakashi Iwai #else
95080e0a7c0STakashi Iwai 	static const int possible_irqs[] = {9, 10, 11, 7, -1};
95199a0b768STakashi Iwai #endif	/* OPTi93X */
95280e0a7c0STakashi Iwai 	static const int possible_mpu_irqs[] = {5, 9, 10, 7, -1};
95380e0a7c0STakashi Iwai 	static const int possible_dma1s[] = {3, 1, 0, -1};
95499a0b768STakashi Iwai #if defined(CS4231) || defined(OPTi93X)
95580e0a7c0STakashi Iwai 	static const int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
95699a0b768STakashi Iwai #endif	/* CS4231 || OPTi93X */
95799a0b768STakashi Iwai 
95899a0b768STakashi Iwai 	if (mpu_port == SNDRV_AUTO_PORT) {
959913ad3a3STakashi Iwai 		mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2);
960913ad3a3STakashi Iwai 		if (mpu_port < 0) {
96199a0b768STakashi Iwai 			snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
96299a0b768STakashi Iwai 			return -EBUSY;
96399a0b768STakashi Iwai 		}
96499a0b768STakashi Iwai 	}
96599a0b768STakashi Iwai 	if (irq == SNDRV_AUTO_IRQ) {
966913ad3a3STakashi Iwai 		irq = snd_legacy_find_free_irq(possible_irqs);
967913ad3a3STakashi Iwai 		if (irq < 0) {
96899a0b768STakashi Iwai 			snd_printk(KERN_ERR "unable to find a free IRQ\n");
96999a0b768STakashi Iwai 			return -EBUSY;
97099a0b768STakashi Iwai 		}
97199a0b768STakashi Iwai 	}
97299a0b768STakashi Iwai 	if (mpu_irq == SNDRV_AUTO_IRQ) {
973913ad3a3STakashi Iwai 		mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs);
974913ad3a3STakashi Iwai 		if (mpu_irq < 0) {
97599a0b768STakashi Iwai 			snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
97699a0b768STakashi Iwai 			return -EBUSY;
97799a0b768STakashi Iwai 		}
97899a0b768STakashi Iwai 	}
97999a0b768STakashi Iwai 	if (dma1 == SNDRV_AUTO_DMA) {
980913ad3a3STakashi Iwai 		dma1 = snd_legacy_find_free_dma(possible_dma1s);
981913ad3a3STakashi Iwai 		if (dma1 < 0) {
98299a0b768STakashi Iwai 			snd_printk(KERN_ERR "unable to find a free DMA1\n");
98399a0b768STakashi Iwai 			return -EBUSY;
98499a0b768STakashi Iwai 		}
98599a0b768STakashi Iwai 	}
98699a0b768STakashi Iwai #if defined(CS4231) || defined(OPTi93X)
98799a0b768STakashi Iwai 	if (dma2 == SNDRV_AUTO_DMA) {
988913ad3a3STakashi Iwai 		dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]);
989913ad3a3STakashi Iwai 		if (dma2 < 0) {
9904c9f1d3eSTakashi Iwai 			snd_printk(KERN_ERR "unable to find a free DMA2\n");
99199a0b768STakashi Iwai 			return -EBUSY;
99299a0b768STakashi Iwai 		}
99399a0b768STakashi Iwai 	}
99499a0b768STakashi Iwai #endif
99599a0b768STakashi Iwai 
9964323cc4dSTakashi Iwai 	error = snd_opti9xx_card_new(devptr, &card);
9973e7fb9f7STakashi Iwai 	if (error < 0)
9983e7fb9f7STakashi Iwai 		return error;
99999a0b768STakashi Iwai 
1000913ad3a3STakashi Iwai 	error = snd_card_opti9xx_detect(card, card->private_data);
10012973ee4aSTakashi Iwai 	if (error < 0)
10021da177e4SLinus Torvalds 		return error;
1003913ad3a3STakashi Iwai 	error = snd_opti9xx_probe(card);
10042973ee4aSTakashi Iwai 	if (error < 0)
100599a0b768STakashi Iwai 		return error;
10065e24c1c1STakashi Iwai 	dev_set_drvdata(devptr, card);
10071da177e4SLinus Torvalds 	return 0;
10081da177e4SLinus Torvalds }
10091da177e4SLinus Torvalds 
10105dd25072SOndrej Zary #ifdef CONFIG_PM
10115dd25072SOndrej Zary static int snd_opti9xx_suspend(struct snd_card *card)
10125dd25072SOndrej Zary {
10135dd25072SOndrej Zary 	struct snd_opti9xx *chip = card->private_data;
10145dd25072SOndrej Zary 
10155dd25072SOndrej Zary 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
10165dd25072SOndrej Zary 	chip->codec->suspend(chip->codec);
10175dd25072SOndrej Zary 	return 0;
10185dd25072SOndrej Zary }
10195dd25072SOndrej Zary 
10205dd25072SOndrej Zary static int snd_opti9xx_resume(struct snd_card *card)
10215dd25072SOndrej Zary {
10225dd25072SOndrej Zary 	struct snd_opti9xx *chip = card->private_data;
10235dd25072SOndrej Zary 	int error, xdma2;
10245dd25072SOndrej Zary #if defined(CS4231) || defined(OPTi93X)
10255dd25072SOndrej Zary 	xdma2 = dma2;
10265dd25072SOndrej Zary #else
10275dd25072SOndrej Zary 	xdma2 = -1;
10285dd25072SOndrej Zary #endif
10295dd25072SOndrej Zary 
10305dd25072SOndrej Zary 	error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2,
10315dd25072SOndrej Zary 				      mpu_port, mpu_irq);
10325dd25072SOndrej Zary 	if (error)
10335dd25072SOndrej Zary 		return error;
10345dd25072SOndrej Zary 	chip->codec->resume(chip->codec);
10355dd25072SOndrej Zary 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
10365dd25072SOndrej Zary 	return 0;
10375dd25072SOndrej Zary }
10385dd25072SOndrej Zary 
10395dd25072SOndrej Zary static int snd_opti9xx_isa_suspend(struct device *dev, unsigned int n,
10405dd25072SOndrej Zary 				   pm_message_t state)
10415dd25072SOndrej Zary {
10425dd25072SOndrej Zary 	return snd_opti9xx_suspend(dev_get_drvdata(dev));
10435dd25072SOndrej Zary }
10445dd25072SOndrej Zary 
10455dd25072SOndrej Zary static int snd_opti9xx_isa_resume(struct device *dev, unsigned int n)
10465dd25072SOndrej Zary {
10475dd25072SOndrej Zary 	return snd_opti9xx_resume(dev_get_drvdata(dev));
10485dd25072SOndrej Zary }
10495dd25072SOndrej Zary #endif
10505dd25072SOndrej Zary 
10515e24c1c1STakashi Iwai static struct isa_driver snd_opti9xx_driver = {
10525e24c1c1STakashi Iwai 	.match		= snd_opti9xx_isa_match,
10535e24c1c1STakashi Iwai 	.probe		= snd_opti9xx_isa_probe,
10545dd25072SOndrej Zary #ifdef CONFIG_PM
10555dd25072SOndrej Zary 	.suspend	= snd_opti9xx_isa_suspend,
10565dd25072SOndrej Zary 	.resume		= snd_opti9xx_isa_resume,
10575dd25072SOndrej Zary #endif
105899a0b768STakashi Iwai 	.driver		= {
105983c51c0aSRene Herman 		.name	= DEV_NAME
106099a0b768STakashi Iwai 	},
106199a0b768STakashi Iwai };
106299a0b768STakashi Iwai 
10631da177e4SLinus Torvalds #ifdef CONFIG_PNP
10641bff292eSBill Pemberton static int snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
106599a0b768STakashi Iwai 				 const struct pnp_card_device_id *pid)
106699a0b768STakashi Iwai {
106799a0b768STakashi Iwai 	struct snd_card *card;
106899a0b768STakashi Iwai 	int error, hw;
106999a0b768STakashi Iwai 	struct snd_opti9xx *chip;
107099a0b768STakashi Iwai 
107199a0b768STakashi Iwai 	if (snd_opti9xx_pnp_is_probed)
107299a0b768STakashi Iwai 		return -EBUSY;
107399a0b768STakashi Iwai 	if (! isapnp)
107499a0b768STakashi Iwai 		return -ENODEV;
10754323cc4dSTakashi Iwai 	error = snd_opti9xx_card_new(&pcard->card->dev, &card);
10763e7fb9f7STakashi Iwai 	if (error < 0)
10773e7fb9f7STakashi Iwai 		return error;
107899a0b768STakashi Iwai 	chip = card->private_data;
107999a0b768STakashi Iwai 
108099a0b768STakashi Iwai 	hw = snd_card_opti9xx_pnp(chip, pcard, pid);
108199a0b768STakashi Iwai 	switch (hw) {
108299a0b768STakashi Iwai 	case 0x0924:
108399a0b768STakashi Iwai 		hw = OPTi9XX_HW_82C924;
108499a0b768STakashi Iwai 		break;
108599a0b768STakashi Iwai 	case 0x0925:
108699a0b768STakashi Iwai 		hw = OPTi9XX_HW_82C925;
108799a0b768STakashi Iwai 		break;
108899a0b768STakashi Iwai 	case 0x0931:
108999a0b768STakashi Iwai 		hw = OPTi9XX_HW_82C931;
109099a0b768STakashi Iwai 		break;
109199a0b768STakashi Iwai 	default:
109299a0b768STakashi Iwai 		return -ENODEV;
109399a0b768STakashi Iwai 	}
109499a0b768STakashi Iwai 
1095913ad3a3STakashi Iwai 	error = snd_opti9xx_init(chip, hw);
10962973ee4aSTakashi Iwai 	if (error)
109799a0b768STakashi Iwai 		return error;
10982973ee4aSTakashi Iwai 	error = snd_opti9xx_read_check(card, chip);
1099*e24ef488SColin Ian King 	if (error) {
1100e6960e19SKrzysztof Helt 		snd_printk(KERN_ERR "OPTI chip not found\n");
1101e6960e19SKrzysztof Helt 		return error;
1102*e24ef488SColin Ian King 	}
1103913ad3a3STakashi Iwai 	error = snd_opti9xx_probe(card);
11042973ee4aSTakashi Iwai 	if (error < 0)
110599a0b768STakashi Iwai 		return error;
110699a0b768STakashi Iwai 	pnp_set_card_drvdata(pcard, card);
110799a0b768STakashi Iwai 	snd_opti9xx_pnp_is_probed = 1;
110899a0b768STakashi Iwai 	return 0;
110999a0b768STakashi Iwai }
111099a0b768STakashi Iwai 
11111bff292eSBill Pemberton static void snd_opti9xx_pnp_remove(struct pnp_card_link *pcard)
11121da177e4SLinus Torvalds {
111399a0b768STakashi Iwai 	snd_opti9xx_pnp_is_probed = 0;
11141da177e4SLinus Torvalds }
11151da177e4SLinus Torvalds 
11165dd25072SOndrej Zary #ifdef CONFIG_PM
11175dd25072SOndrej Zary static int snd_opti9xx_pnp_suspend(struct pnp_card_link *pcard,
11185dd25072SOndrej Zary 				   pm_message_t state)
11195dd25072SOndrej Zary {
11205dd25072SOndrej Zary 	return snd_opti9xx_suspend(pnp_get_card_drvdata(pcard));
11215dd25072SOndrej Zary }
11225dd25072SOndrej Zary 
11235dd25072SOndrej Zary static int snd_opti9xx_pnp_resume(struct pnp_card_link *pcard)
11245dd25072SOndrej Zary {
11255dd25072SOndrej Zary 	return snd_opti9xx_resume(pnp_get_card_drvdata(pcard));
11265dd25072SOndrej Zary }
11275dd25072SOndrej Zary #endif
11285dd25072SOndrej Zary 
11291da177e4SLinus Torvalds static struct pnp_card_driver opti9xx_pnpc_driver = {
11301da177e4SLinus Torvalds 	.flags		= PNP_DRIVER_RES_DISABLE,
1131fb615499STakashi Iwai 	.name		= DEV_NAME,
11321da177e4SLinus Torvalds 	.id_table	= snd_opti9xx_pnpids,
113399a0b768STakashi Iwai 	.probe		= snd_opti9xx_pnp_probe,
11341bff292eSBill Pemberton 	.remove		= snd_opti9xx_pnp_remove,
11355dd25072SOndrej Zary #ifdef CONFIG_PM
11365dd25072SOndrej Zary 	.suspend	= snd_opti9xx_pnp_suspend,
11375dd25072SOndrej Zary 	.resume		= snd_opti9xx_pnp_resume,
11385dd25072SOndrej Zary #endif
11391da177e4SLinus Torvalds };
11401da177e4SLinus Torvalds #endif
11411da177e4SLinus Torvalds 
114299a0b768STakashi Iwai #ifdef OPTi93X
114399a0b768STakashi Iwai #define CHIP_NAME	"82C93x"
114499a0b768STakashi Iwai #else
114599a0b768STakashi Iwai #define CHIP_NAME	"82C92x"
114699a0b768STakashi Iwai #endif
114799a0b768STakashi Iwai 
11481da177e4SLinus Torvalds static int __init alsa_card_opti9xx_init(void)
11491da177e4SLinus Torvalds {
11500bbbc4caSTakashi Iwai #ifdef CONFIG_PNP
115199a0b768STakashi Iwai 	pnp_register_card_driver(&opti9xx_pnpc_driver);
115299a0b768STakashi Iwai 	if (snd_opti9xx_pnp_is_probed)
11531da177e4SLinus Torvalds 		return 0;
1154101f6f4bSTakashi Iwai 	pnp_unregister_card_driver(&opti9xx_pnpc_driver);
11550bbbc4caSTakashi Iwai #endif
11565e24c1c1STakashi Iwai 	return isa_register_driver(&snd_opti9xx_driver, 1);
11571da177e4SLinus Torvalds }
11581da177e4SLinus Torvalds 
11591da177e4SLinus Torvalds static void __exit alsa_card_opti9xx_exit(void)
11601da177e4SLinus Torvalds {
1161f7a9275dSClemens Ladisch 	if (!snd_opti9xx_pnp_is_probed) {
11625e24c1c1STakashi Iwai 		isa_unregister_driver(&snd_opti9xx_driver);
11635e24c1c1STakashi Iwai 		return;
1164f7a9275dSClemens Ladisch 	}
11650bbbc4caSTakashi Iwai #ifdef CONFIG_PNP
11661da177e4SLinus Torvalds 	pnp_unregister_card_driver(&opti9xx_pnpc_driver);
11670bbbc4caSTakashi Iwai #endif
11681da177e4SLinus Torvalds }
11691da177e4SLinus Torvalds 
11701da177e4SLinus Torvalds module_init(alsa_card_opti9xx_init)
11711da177e4SLinus Torvalds module_exit(alsa_card_opti9xx_exit)
1172