xref: /linux/sound/isa/opti9xx/opti92x-ad1848.c (revision 4c9f1d3ed7e5f910b66dc4d1456cfac17e58cf0e)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds     card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards.
31da177e4SLinus Torvalds     Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it>
41da177e4SLinus Torvalds 
51da177e4SLinus Torvalds     Part of this code was developed at the Italian Ministry of Air Defence,
61da177e4SLinus Torvalds     Sixth Division (oh, che pace ...), Rome.
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds     Thanks to Maria Grazia Pollarini, Salvatore Vassallo.
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds     This program is free software; you can redistribute it and/or modify
111da177e4SLinus Torvalds     it under the terms of the GNU General Public License as published by
121da177e4SLinus Torvalds     the Free Software Foundation; either version 2 of the License, or
131da177e4SLinus Torvalds     (at your option) any later version.
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds     This program is distributed in the hope that it will be useful,
161da177e4SLinus Torvalds     but WITHOUT ANY WARRANTY; without even the implied warranty of
171da177e4SLinus Torvalds     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
181da177e4SLinus Torvalds     GNU General Public License for more details.
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds     You should have received a copy of the GNU General Public License
211da177e4SLinus Torvalds     along with this program; if not, write to the Free Software
221da177e4SLinus Torvalds     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
231da177e4SLinus Torvalds */
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds #include <linux/init.h>
2799a0b768STakashi Iwai #include <linux/err.h>
285e24c1c1STakashi Iwai #include <linux/isa.h>
2999a0b768STakashi Iwai #include <linux/delay.h>
301da177e4SLinus Torvalds #include <linux/slab.h>
311da177e4SLinus Torvalds #include <linux/pnp.h>
321da177e4SLinus Torvalds #include <linux/moduleparam.h>
3399a0b768STakashi Iwai #include <asm/io.h>
3499a0b768STakashi Iwai #include <asm/dma.h>
351da177e4SLinus Torvalds #include <sound/core.h>
3661ef19d7SKrzysztof Helt #include <sound/wss.h>
371da177e4SLinus Torvalds #include <sound/mpu401.h>
381da177e4SLinus Torvalds #include <sound/opl3.h>
391da177e4SLinus Torvalds #ifndef OPTi93X
401da177e4SLinus Torvalds #include <sound/opl4.h>
411da177e4SLinus Torvalds #endif
421da177e4SLinus Torvalds #define SNDRV_LEGACY_FIND_FREE_IRQ
431da177e4SLinus Torvalds #define SNDRV_LEGACY_FIND_FREE_DMA
441da177e4SLinus Torvalds #include <sound/initval.h>
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
471da177e4SLinus Torvalds MODULE_LICENSE("GPL");
481da177e4SLinus Torvalds #ifdef OPTi93X
491da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi93X");
501da177e4SLinus Torvalds MODULE_SUPPORTED_DEVICE("{{OPTi,82C931/3}}");
511da177e4SLinus Torvalds #else	/* OPTi93X */
521da177e4SLinus Torvalds #ifdef CS4231
531da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi92X - CS4231");
541da177e4SLinus Torvalds MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (CS4231)},"
551da177e4SLinus Torvalds 		"{OPTi,82C925 (CS4231)}}");
561da177e4SLinus Torvalds #else	/* CS4231 */
571da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi92X - AD1848");
581da177e4SLinus Torvalds MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)},"
591da177e4SLinus Torvalds 		"{OPTi,82C925 (AD1848)},"
601da177e4SLinus Torvalds 	        "{OAK,Mozart}}");
611da177e4SLinus Torvalds #endif	/* CS4231 */
621da177e4SLinus Torvalds #endif	/* OPTi93X */
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
651da177e4SLinus Torvalds static char *id = SNDRV_DEFAULT_STR1;		/* ID for this card */
661da177e4SLinus Torvalds //static int enable = SNDRV_DEFAULT_ENABLE1;	/* Enable this card */
6751f6baadSRene Herman #ifdef CONFIG_PNP
681da177e4SLinus Torvalds static int isapnp = 1;			/* Enable ISA PnP detection */
6951f6baadSRene Herman #endif
701da177e4SLinus Torvalds static long port = SNDRV_DEFAULT_PORT1; 	/* 0x530,0xe80,0xf40,0x604 */
711da177e4SLinus Torvalds static long mpu_port = SNDRV_DEFAULT_PORT1;	/* 0x300,0x310,0x320,0x330 */
721da177e4SLinus Torvalds static long fm_port = SNDRV_DEFAULT_PORT1;	/* 0x388 */
731da177e4SLinus Torvalds static int irq = SNDRV_DEFAULT_IRQ1;		/* 5,7,9,10,11 */
741da177e4SLinus Torvalds static int mpu_irq = SNDRV_DEFAULT_IRQ1;	/* 5,7,9,10 */
751da177e4SLinus Torvalds static int dma1 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
761da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
771da177e4SLinus Torvalds static int dma2 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
781da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds module_param(index, int, 0444);
811da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for opti9xx based soundcard.");
821da177e4SLinus Torvalds module_param(id, charp, 0444);
831da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard.");
841da177e4SLinus Torvalds //module_param(enable, bool, 0444);
851da177e4SLinus Torvalds //MODULE_PARM_DESC(enable, "Enable opti9xx soundcard.");
8651f6baadSRene Herman #ifdef CONFIG_PNP
871da177e4SLinus Torvalds module_param(isapnp, bool, 0444);
881da177e4SLinus Torvalds MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
8951f6baadSRene Herman #endif
901da177e4SLinus Torvalds module_param(port, long, 0444);
911da177e4SLinus Torvalds MODULE_PARM_DESC(port, "WSS port # for opti9xx driver.");
921da177e4SLinus Torvalds module_param(mpu_port, long, 0444);
931da177e4SLinus Torvalds MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver.");
941da177e4SLinus Torvalds module_param(fm_port, long, 0444);
951da177e4SLinus Torvalds MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver.");
961da177e4SLinus Torvalds module_param(irq, int, 0444);
971da177e4SLinus Torvalds MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver.");
981da177e4SLinus Torvalds module_param(mpu_irq, int, 0444);
991da177e4SLinus Torvalds MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver.");
1001da177e4SLinus Torvalds module_param(dma1, int, 0444);
1011da177e4SLinus Torvalds MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver.");
1021da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
1031da177e4SLinus Torvalds module_param(dma2, int, 0444);
1041da177e4SLinus Torvalds MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
1051da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds #define OPTi9XX_HW_82C928	1
1081da177e4SLinus Torvalds #define OPTi9XX_HW_82C929	2
1091da177e4SLinus Torvalds #define OPTi9XX_HW_82C924	3
1101da177e4SLinus Torvalds #define OPTi9XX_HW_82C925	4
1111da177e4SLinus Torvalds #define OPTi9XX_HW_82C930	5
1121da177e4SLinus Torvalds #define OPTi9XX_HW_82C931	6
1131da177e4SLinus Torvalds #define OPTi9XX_HW_82C933	7
1141da177e4SLinus Torvalds #define OPTi9XX_HW_LAST		OPTi9XX_HW_82C933
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds #define OPTi9XX_MC_REG(n)	n
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds #ifdef OPTi93X
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds #define OPTi93X_STATUS			0x02
1211da177e4SLinus Torvalds #define OPTi93X_PORT(chip, r)		((chip)->port + OPTi93X_##r)
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds #define OPTi93X_IRQ_PLAYBACK		0x04
1241da177e4SLinus Torvalds #define OPTi93X_IRQ_CAPTURE		0x08
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds #endif /* OPTi93X */
1271da177e4SLinus Torvalds 
128346c7a68STakashi Iwai struct snd_opti9xx {
1291da177e4SLinus Torvalds 	unsigned short hardware;
1301da177e4SLinus Torvalds 	unsigned char password;
1311da177e4SLinus Torvalds 	char name[7];
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 	unsigned long mc_base;
1341da177e4SLinus Torvalds 	struct resource *res_mc_base;
1351da177e4SLinus Torvalds 	unsigned long mc_base_size;
1361da177e4SLinus Torvalds #ifdef OPTi93X
1371da177e4SLinus Torvalds 	unsigned long mc_indir_index;
1387779f75fSKrzysztof Helt 	struct snd_wss *codec;
1391da177e4SLinus Torvalds #endif	/* OPTi93X */
1401da177e4SLinus Torvalds 	unsigned long pwd_reg;
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 	spinlock_t lock;
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds 	long wss_base;
1451da177e4SLinus Torvalds 	int irq;
1461da177e4SLinus Torvalds 	int dma1;
1471da177e4SLinus Torvalds 	int dma2;
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds 	long fm_port;
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds 	long mpu_port;
1521da177e4SLinus Torvalds 	int mpu_irq;
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds #ifdef CONFIG_PNP
1551da177e4SLinus Torvalds 	struct pnp_dev *dev;
1561da177e4SLinus Torvalds 	struct pnp_dev *devmpu;
1571da177e4SLinus Torvalds #endif	/* CONFIG_PNP */
1581da177e4SLinus Torvalds };
1591da177e4SLinus Torvalds 
16099a0b768STakashi Iwai static int snd_opti9xx_pnp_is_probed;
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds #ifdef CONFIG_PNP
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds static struct pnp_card_device_id snd_opti9xx_pnpids[] = {
1651da177e4SLinus Torvalds #ifndef OPTi93X
1661da177e4SLinus Torvalds 	/* OPTi 82C924 */
1671da177e4SLinus Torvalds 	{ .id = "OPT0924", .devs = { { "OPT0000" }, { "OPT0002" } }, .driver_data = 0x0924 },
1681da177e4SLinus Torvalds 	/* OPTi 82C925 */
1691da177e4SLinus Torvalds 	{ .id = "OPT0925", .devs = { { "OPT9250" }, { "OPT0002" } }, .driver_data = 0x0925 },
1701da177e4SLinus Torvalds #else
1711da177e4SLinus Torvalds 	/* OPTi 82C931/3 */
1721da177e4SLinus Torvalds 	{ .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } }, .driver_data = 0x0931 },
1731da177e4SLinus Torvalds #endif	/* OPTi93X */
1741da177e4SLinus Torvalds 	{ .id = "" }
1751da177e4SLinus Torvalds };
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids);
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds #endif	/* CONFIG_PNP */
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds #ifdef OPTi93X
18283c51c0aSRene Herman #define DEV_NAME "opti93x"
1831da177e4SLinus Torvalds #else
18483c51c0aSRene Herman #define DEV_NAME "opti92x"
18583c51c0aSRene Herman #endif
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds static char * snd_opti9xx_names[] = {
1881da177e4SLinus Torvalds 	"unkown",
1891da177e4SLinus Torvalds 	"82C928",	"82C929",
1901da177e4SLinus Torvalds 	"82C924",	"82C925",
1911da177e4SLinus Torvalds 	"82C930",	"82C931",	"82C933"
1921da177e4SLinus Torvalds };
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds 
1955e24c1c1STakashi Iwai static long __devinit snd_legacy_find_free_ioport(long *port_table, long size)
1961da177e4SLinus Torvalds {
1971da177e4SLinus Torvalds 	while (*port_table != -1) {
198b1d5776dSTakashi Iwai 		if (request_region(*port_table, size, "ALSA test")) {
199b1d5776dSTakashi Iwai 			release_region(*port_table, size);
2001da177e4SLinus Torvalds 			return *port_table;
2011da177e4SLinus Torvalds 		}
2021da177e4SLinus Torvalds 		port_table++;
2031da177e4SLinus Torvalds 	}
2041da177e4SLinus Torvalds 	return -1;
2051da177e4SLinus Torvalds }
2061da177e4SLinus Torvalds 
2075e24c1c1STakashi Iwai static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
2085e24c1c1STakashi Iwai 				      unsigned short hardware)
2091da177e4SLinus Torvalds {
2101da177e4SLinus Torvalds 	static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds 	chip->hardware = hardware;
2131da177e4SLinus Torvalds 	strcpy(chip->name, snd_opti9xx_names[hardware]);
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds 	chip->mc_base_size = opti9xx_mc_size[hardware];
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds 	spin_lock_init(&chip->lock);
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds 	chip->wss_base = -1;
2201da177e4SLinus Torvalds 	chip->irq = -1;
2211da177e4SLinus Torvalds 	chip->dma1 = -1;
2221da177e4SLinus Torvalds 	chip->dma2 = -1;
2231da177e4SLinus Torvalds 	chip->fm_port = -1;
2241da177e4SLinus Torvalds 	chip->mpu_port = -1;
2251da177e4SLinus Torvalds 	chip->mpu_irq = -1;
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds 	switch (hardware) {
2281da177e4SLinus Torvalds #ifndef OPTi93X
2291da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
2301da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
2311da177e4SLinus Torvalds 		chip->mc_base = 0xf8c;
2321da177e4SLinus Torvalds 		chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3;
2331da177e4SLinus Torvalds 		chip->pwd_reg = 3;
2341da177e4SLinus Torvalds 		break;
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
2371da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
2381da177e4SLinus Torvalds 		chip->mc_base = 0xf8c;
2391da177e4SLinus Torvalds 		chip->password = 0xe5;
2401da177e4SLinus Torvalds 		chip->pwd_reg = 3;
2411da177e4SLinus Torvalds 		break;
2421da177e4SLinus Torvalds #else	/* OPTi93X */
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930:
2451da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
2461da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
2471da177e4SLinus Torvalds 		chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d;
2481da177e4SLinus Torvalds 		chip->mc_indir_index = 0xe0e;
2491da177e4SLinus Torvalds 		chip->password = 0xe4;
2501da177e4SLinus Torvalds 		chip->pwd_reg = 0;
2511da177e4SLinus Torvalds 		break;
2521da177e4SLinus Torvalds #endif	/* OPTi93X */
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 	default:
255*4c9f1d3eSTakashi Iwai 		snd_printk(KERN_ERR "chip %d not supported\n", hardware);
2561da177e4SLinus Torvalds 		return -ENODEV;
2571da177e4SLinus Torvalds 	}
2581da177e4SLinus Torvalds 	return 0;
2591da177e4SLinus Torvalds }
2601da177e4SLinus Torvalds 
261346c7a68STakashi Iwai static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,
2621da177e4SLinus Torvalds 				      unsigned char reg)
2631da177e4SLinus Torvalds {
2641da177e4SLinus Torvalds 	unsigned long flags;
2651da177e4SLinus Torvalds 	unsigned char retval = 0xff;
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
2681da177e4SLinus Torvalds 	outb(chip->password, chip->mc_base + chip->pwd_reg);
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds 	switch (chip->hardware) {
2711da177e4SLinus Torvalds #ifndef OPTi93X
2721da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
2731da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
2741da177e4SLinus Torvalds 		if (reg > 7) {
2751da177e4SLinus Torvalds 			outb(reg, chip->mc_base + 8);
2761da177e4SLinus Torvalds 			outb(chip->password, chip->mc_base + chip->pwd_reg);
2771da177e4SLinus Torvalds 			retval = inb(chip->mc_base + 9);
2781da177e4SLinus Torvalds 			break;
2791da177e4SLinus Torvalds 		}
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
2821da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
2831da177e4SLinus Torvalds 		retval = inb(chip->mc_base + reg);
2841da177e4SLinus Torvalds 		break;
2851da177e4SLinus Torvalds #else	/* OPTi93X */
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930:
2881da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
2891da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
2901da177e4SLinus Torvalds 		outb(reg, chip->mc_indir_index);
2911da177e4SLinus Torvalds 		outb(chip->password, chip->mc_base + chip->pwd_reg);
2921da177e4SLinus Torvalds 		retval = inb(chip->mc_indir_index + 1);
2931da177e4SLinus Torvalds 		break;
2941da177e4SLinus Torvalds #endif	/* OPTi93X */
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds 	default:
297*4c9f1d3eSTakashi Iwai 		snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
2981da177e4SLinus Torvalds 	}
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
3011da177e4SLinus Torvalds 	return retval;
3021da177e4SLinus Torvalds }
3031da177e4SLinus Torvalds 
304346c7a68STakashi Iwai static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
3051da177e4SLinus Torvalds 			      unsigned char value)
3061da177e4SLinus Torvalds {
3071da177e4SLinus Torvalds 	unsigned long flags;
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
3101da177e4SLinus Torvalds 	outb(chip->password, chip->mc_base + chip->pwd_reg);
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 	switch (chip->hardware) {
3131da177e4SLinus Torvalds #ifndef OPTi93X
3141da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
3151da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
3161da177e4SLinus Torvalds 		if (reg > 7) {
3171da177e4SLinus Torvalds 			outb(reg, chip->mc_base + 8);
3181da177e4SLinus Torvalds 			outb(chip->password, chip->mc_base + chip->pwd_reg);
3191da177e4SLinus Torvalds 			outb(value, chip->mc_base + 9);
3201da177e4SLinus Torvalds 			break;
3211da177e4SLinus Torvalds 		}
3221da177e4SLinus Torvalds 
3231da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
3241da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
3251da177e4SLinus Torvalds 		outb(value, chip->mc_base + reg);
3261da177e4SLinus Torvalds 		break;
3271da177e4SLinus Torvalds #else	/* OPTi93X */
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930:
3301da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
3311da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
3321da177e4SLinus Torvalds 		outb(reg, chip->mc_indir_index);
3331da177e4SLinus Torvalds 		outb(chip->password, chip->mc_base + chip->pwd_reg);
3341da177e4SLinus Torvalds 		outb(value, chip->mc_indir_index + 1);
3351da177e4SLinus Torvalds 		break;
3361da177e4SLinus Torvalds #endif	/* OPTi93X */
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds 	default:
339*4c9f1d3eSTakashi Iwai 		snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
3401da177e4SLinus Torvalds 	}
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
3431da177e4SLinus Torvalds }
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds #define snd_opti9xx_write_mask(chip, reg, value, mask)	\
3471da177e4SLinus Torvalds 	snd_opti9xx_write(chip, reg,			\
3481da177e4SLinus Torvalds 		(snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))
3491da177e4SLinus Torvalds 
3501da177e4SLinus Torvalds 
3515e24c1c1STakashi Iwai static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
3521da177e4SLinus Torvalds {
3531da177e4SLinus Torvalds 	unsigned char wss_base_bits;
3541da177e4SLinus Torvalds 	unsigned char irq_bits;
3551da177e4SLinus Torvalds 	unsigned char dma_bits;
3561da177e4SLinus Torvalds 	unsigned char mpu_port_bits = 0;
3571da177e4SLinus Torvalds 	unsigned char mpu_irq_bits;
3581da177e4SLinus Torvalds 
3591da177e4SLinus Torvalds 	switch (chip->hardware) {
3601da177e4SLinus Torvalds #ifndef OPTi93X
3611da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
3621da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc);
3631da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
3661da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
3671da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
3681da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
3691da177e4SLinus Torvalds #ifdef CS4231
3701da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
3711da177e4SLinus Torvalds #else
3721da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
3731da177e4SLinus Torvalds #endif	/* CS4231 */
3741da177e4SLinus Torvalds 		break;
3751da177e4SLinus Torvalds 
3761da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
3771da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
3781da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
3791da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
3801da177e4SLinus Torvalds 		/*
3811da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae);
3821da177e4SLinus Torvalds 		*/
3831da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
3841da177e4SLinus Torvalds #ifdef CS4231
3851da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
3861da177e4SLinus Torvalds #else
3871da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
3881da177e4SLinus Torvalds #endif	/* CS4231 */
3891da177e4SLinus Torvalds 		break;
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds #else	/* OPTi93X */
3921da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
3931da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
394f81b953dSKrzysztof Helt 		/*
395f81b953dSKrzysztof Helt 		 * The BTC 1817DW has QS1000 wavetable which is connected
396f81b953dSKrzysztof Helt 		 * to the serial digital input of the OPTI931.
397f81b953dSKrzysztof Helt 		 */
398f81b953dSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(21), 0x82, 0xff);
399f81b953dSKrzysztof Helt 		/*
400f81b953dSKrzysztof Helt 		 * This bit sets OPTI931 to automaticaly select FM
401f81b953dSKrzysztof Helt 		 * or digital input signal.
402f81b953dSKrzysztof Helt 		 */
403f81b953dSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01);
4043ae5f36aSKrzysztof Helt 	case OPTi9XX_HW_82C930: /* FALL THROUGH */
4053ae5f36aSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
4063ae5f36aSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
4073ae5f36aSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
4083ae5f36aSKrzysztof Helt 			(chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
4093ae5f36aSKrzysztof Helt 			0x34);
4103ae5f36aSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
4111da177e4SLinus Torvalds 		break;
4121da177e4SLinus Torvalds #endif	/* OPTi93X */
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds 	default:
415*4c9f1d3eSTakashi Iwai 		snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
4161da177e4SLinus Torvalds 		return -EINVAL;
4171da177e4SLinus Torvalds 	}
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds 	switch (chip->wss_base) {
4201da177e4SLinus Torvalds 	case 0x530:
4211da177e4SLinus Torvalds 		wss_base_bits = 0x00;
4221da177e4SLinus Torvalds 		break;
4231da177e4SLinus Torvalds 	case 0x604:
4241da177e4SLinus Torvalds 		wss_base_bits = 0x03;
4251da177e4SLinus Torvalds 		break;
4261da177e4SLinus Torvalds 	case 0xe80:
4271da177e4SLinus Torvalds 		wss_base_bits = 0x01;
4281da177e4SLinus Torvalds 		break;
4291da177e4SLinus Torvalds 	case 0xf40:
4301da177e4SLinus Torvalds 		wss_base_bits = 0x02;
4311da177e4SLinus Torvalds 		break;
4321da177e4SLinus Torvalds 	default:
433*4c9f1d3eSTakashi Iwai 		snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n",
434*4c9f1d3eSTakashi Iwai 			   chip->wss_base);
4351da177e4SLinus Torvalds 		goto __skip_base;
4361da177e4SLinus Torvalds 	}
4371da177e4SLinus Torvalds 	snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds __skip_base:
4401da177e4SLinus Torvalds 	switch (chip->irq) {
4411da177e4SLinus Torvalds //#ifdef OPTi93X
4421da177e4SLinus Torvalds 	case 5:
4431da177e4SLinus Torvalds 		irq_bits = 0x05;
4441da177e4SLinus Torvalds 		break;
4451da177e4SLinus Torvalds //#endif	/* OPTi93X */
4461da177e4SLinus Torvalds 	case 7:
4471da177e4SLinus Torvalds 		irq_bits = 0x01;
4481da177e4SLinus Torvalds 		break;
4491da177e4SLinus Torvalds 	case 9:
4501da177e4SLinus Torvalds 		irq_bits = 0x02;
4511da177e4SLinus Torvalds 		break;
4521da177e4SLinus Torvalds 	case 10:
4531da177e4SLinus Torvalds 		irq_bits = 0x03;
4541da177e4SLinus Torvalds 		break;
4551da177e4SLinus Torvalds 	case 11:
4561da177e4SLinus Torvalds 		irq_bits = 0x04;
4571da177e4SLinus Torvalds 		break;
4581da177e4SLinus Torvalds 	default:
459*4c9f1d3eSTakashi Iwai 		snd_printk(KERN_WARNING "WSS irq # %d not valid\n", chip->irq);
4601da177e4SLinus Torvalds 		goto __skip_resources;
4611da177e4SLinus Torvalds 	}
4621da177e4SLinus Torvalds 
4631da177e4SLinus Torvalds 	switch (chip->dma1) {
4641da177e4SLinus Torvalds 	case 0:
4651da177e4SLinus Torvalds 		dma_bits = 0x01;
4661da177e4SLinus Torvalds 		break;
4671da177e4SLinus Torvalds 	case 1:
4681da177e4SLinus Torvalds 		dma_bits = 0x02;
4691da177e4SLinus Torvalds 		break;
4701da177e4SLinus Torvalds 	case 3:
4711da177e4SLinus Torvalds 		dma_bits = 0x03;
4721da177e4SLinus Torvalds 		break;
4731da177e4SLinus Torvalds 	default:
474*4c9f1d3eSTakashi Iwai 		snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n",
475*4c9f1d3eSTakashi Iwai 			   chip->dma1);
4761da177e4SLinus Torvalds 		goto __skip_resources;
4771da177e4SLinus Torvalds 	}
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
4801da177e4SLinus Torvalds 	if (chip->dma1 == chip->dma2) {
481*4c9f1d3eSTakashi Iwai 		snd_printk(KERN_ERR "don't want to share dmas\n");
4821da177e4SLinus Torvalds 		return -EBUSY;
4831da177e4SLinus Torvalds 	}
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 	switch (chip->dma2) {
4861da177e4SLinus Torvalds 	case 0:
4871da177e4SLinus Torvalds 	case 1:
4881da177e4SLinus Torvalds 		break;
4891da177e4SLinus Torvalds 	default:
490*4c9f1d3eSTakashi Iwai 		snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n",
491*4c9f1d3eSTakashi Iwai 			   chip->dma2);
4921da177e4SLinus Torvalds 		goto __skip_resources;
4931da177e4SLinus Torvalds 	}
4941da177e4SLinus Torvalds 	dma_bits |= 0x04;
4951da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
4961da177e4SLinus Torvalds 
4971da177e4SLinus Torvalds #ifndef OPTi93X
4981da177e4SLinus Torvalds 	 outb(irq_bits << 3 | dma_bits, chip->wss_base);
4991da177e4SLinus Torvalds #else /* OPTi93X */
5001da177e4SLinus Torvalds 	snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));
5011da177e4SLinus Torvalds #endif /* OPTi93X */
5021da177e4SLinus Torvalds 
5031da177e4SLinus Torvalds __skip_resources:
5041da177e4SLinus Torvalds 	if (chip->hardware > OPTi9XX_HW_82C928) {
5051da177e4SLinus Torvalds 		switch (chip->mpu_port) {
5061da177e4SLinus Torvalds 		case 0:
5071da177e4SLinus Torvalds 		case -1:
5081da177e4SLinus Torvalds 			break;
5091da177e4SLinus Torvalds 		case 0x300:
5101da177e4SLinus Torvalds 			mpu_port_bits = 0x03;
5111da177e4SLinus Torvalds 			break;
5121da177e4SLinus Torvalds 		case 0x310:
5131da177e4SLinus Torvalds 			mpu_port_bits = 0x02;
5141da177e4SLinus Torvalds 			break;
5151da177e4SLinus Torvalds 		case 0x320:
5161da177e4SLinus Torvalds 			mpu_port_bits = 0x01;
5171da177e4SLinus Torvalds 			break;
5181da177e4SLinus Torvalds 		case 0x330:
5191da177e4SLinus Torvalds 			mpu_port_bits = 0x00;
5201da177e4SLinus Torvalds 			break;
5211da177e4SLinus Torvalds 		default:
522*4c9f1d3eSTakashi Iwai 			snd_printk(KERN_WARNING
523*4c9f1d3eSTakashi Iwai 				   "MPU-401 port 0x%lx not valid\n",
5241da177e4SLinus Torvalds 				chip->mpu_port);
5251da177e4SLinus Torvalds 			goto __skip_mpu;
5261da177e4SLinus Torvalds 		}
5271da177e4SLinus Torvalds 
5281da177e4SLinus Torvalds 		switch (chip->mpu_irq) {
5291da177e4SLinus Torvalds 		case 5:
5301da177e4SLinus Torvalds 			mpu_irq_bits = 0x02;
5311da177e4SLinus Torvalds 			break;
5321da177e4SLinus Torvalds 		case 7:
5331da177e4SLinus Torvalds 			mpu_irq_bits = 0x03;
5341da177e4SLinus Torvalds 			break;
5351da177e4SLinus Torvalds 		case 9:
5361da177e4SLinus Torvalds 			mpu_irq_bits = 0x00;
5371da177e4SLinus Torvalds 			break;
5381da177e4SLinus Torvalds 		case 10:
5391da177e4SLinus Torvalds 			mpu_irq_bits = 0x01;
5401da177e4SLinus Torvalds 			break;
5411da177e4SLinus Torvalds 		default:
542*4c9f1d3eSTakashi Iwai 			snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n",
5431da177e4SLinus Torvalds 				chip->mpu_irq);
5441da177e4SLinus Torvalds 			goto __skip_mpu;
5451da177e4SLinus Torvalds 		}
5461da177e4SLinus Torvalds 
5471da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6),
5481da177e4SLinus Torvalds 			(chip->mpu_port <= 0) ? 0x00 :
5491da177e4SLinus Torvalds 				0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
5501da177e4SLinus Torvalds 			0xf8);
5511da177e4SLinus Torvalds 	}
5521da177e4SLinus Torvalds __skip_mpu:
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 	return 0;
5551da177e4SLinus Torvalds }
5561da177e4SLinus Torvalds 
5571da177e4SLinus Torvalds #ifdef OPTi93X
5581da177e4SLinus Torvalds 
5597d12e780SDavid Howells static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
5601da177e4SLinus Torvalds {
5617779f75fSKrzysztof Helt 	struct snd_wss *codec = dev_id;
5629f240a55SKrzysztof Helt 	struct snd_opti9xx *chip = codec->card->private_data;
5631da177e4SLinus Torvalds 	unsigned char status;
5641da177e4SLinus Torvalds 
5659f240a55SKrzysztof Helt 	status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11));
5661da177e4SLinus Torvalds 	if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)
5671da177e4SLinus Torvalds 		snd_pcm_period_elapsed(codec->playback_substream);
5681da177e4SLinus Torvalds 	if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) {
5697779f75fSKrzysztof Helt 		snd_wss_overrange(codec);
5701da177e4SLinus Torvalds 		snd_pcm_period_elapsed(codec->capture_substream);
5711da177e4SLinus Torvalds 	}
5721da177e4SLinus Torvalds 	outb(0x00, OPTi93X_PORT(codec, STATUS));
5731da177e4SLinus Torvalds 	return IRQ_HANDLED;
5741da177e4SLinus Torvalds }
5751da177e4SLinus Torvalds 
5761da177e4SLinus Torvalds #endif /* OPTi93X */
5771da177e4SLinus Torvalds 
5785e24c1c1STakashi Iwai static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
5795e24c1c1STakashi Iwai 					     struct snd_opti9xx *chip)
5801da177e4SLinus Torvalds {
5811da177e4SLinus Torvalds 	int i, err;
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds #ifndef OPTi93X
5841da177e4SLinus Torvalds 	for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {
5851da177e4SLinus Torvalds 		unsigned char value;
5861da177e4SLinus Torvalds 
5871da177e4SLinus Torvalds 		if ((err = snd_opti9xx_init(chip, i)) < 0)
5881da177e4SLinus Torvalds 			return err;
5891da177e4SLinus Torvalds 
5901da177e4SLinus Torvalds 		if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL)
5911da177e4SLinus Torvalds 			continue;
5921da177e4SLinus Torvalds 
5931da177e4SLinus Torvalds 		value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
5941da177e4SLinus Torvalds 		if ((value != 0xff) && (value != inb(chip->mc_base + 1)))
5951da177e4SLinus Torvalds 			if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
5961da177e4SLinus Torvalds 				return 1;
5971da177e4SLinus Torvalds 
598b1d5776dSTakashi Iwai 		release_and_free_resource(chip->res_mc_base);
5991da177e4SLinus Torvalds 		chip->res_mc_base = NULL;
6001da177e4SLinus Torvalds 
6011da177e4SLinus Torvalds 	}
6021da177e4SLinus Torvalds #else	/* OPTi93X */
6031da177e4SLinus Torvalds 	for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {
6041da177e4SLinus Torvalds 		unsigned long flags;
6051da177e4SLinus Torvalds 		unsigned char value;
6061da177e4SLinus Torvalds 
6071da177e4SLinus Torvalds 		if ((err = snd_opti9xx_init(chip, i)) < 0)
6081da177e4SLinus Torvalds 			return err;
6091da177e4SLinus Torvalds 
6101da177e4SLinus Torvalds 		if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL)
6111da177e4SLinus Torvalds 			continue;
6121da177e4SLinus Torvalds 
6131da177e4SLinus Torvalds 		spin_lock_irqsave(&chip->lock, flags);
6141da177e4SLinus Torvalds 		outb(chip->password, chip->mc_base + chip->pwd_reg);
6151da177e4SLinus Torvalds 		outb(((chip->mc_indir_index & (1 << 8)) >> 4) |
6161da177e4SLinus Torvalds 			((chip->mc_indir_index & 0xf0) >> 4), chip->mc_base);
6171da177e4SLinus Torvalds 		spin_unlock_irqrestore(&chip->lock, flags);
6181da177e4SLinus Torvalds 
6191da177e4SLinus Torvalds 		value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));
6201da177e4SLinus Torvalds 		snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);
6211da177e4SLinus Torvalds 		if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)
6221da177e4SLinus Torvalds 			return 1;
6231da177e4SLinus Torvalds 
624b1d5776dSTakashi Iwai 		release_and_free_resource(chip->res_mc_base);
6251da177e4SLinus Torvalds 		chip->res_mc_base = NULL;
6261da177e4SLinus Torvalds 	}
6271da177e4SLinus Torvalds #endif	/* OPTi93X */
6281da177e4SLinus Torvalds 
6291da177e4SLinus Torvalds 	return -ENODEV;
6301da177e4SLinus Torvalds }
6311da177e4SLinus Torvalds 
6321da177e4SLinus Torvalds #ifdef CONFIG_PNP
6335e24c1c1STakashi Iwai static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
6345e24c1c1STakashi Iwai 					  struct pnp_card_link *card,
6351da177e4SLinus Torvalds 					  const struct pnp_card_device_id *pid)
6361da177e4SLinus Torvalds {
6371da177e4SLinus Torvalds 	struct pnp_dev *pdev;
6381da177e4SLinus Torvalds 	int err;
6391da177e4SLinus Torvalds 
6401da177e4SLinus Torvalds 	chip->dev = pnp_request_card_device(card, pid->devs[0].id, NULL);
641109c53f8SRene Herman 	if (chip->dev == NULL)
6421da177e4SLinus Torvalds 		return -EBUSY;
643109c53f8SRene Herman 
6441da177e4SLinus Torvalds 	chip->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
6451da177e4SLinus Torvalds 
6461da177e4SLinus Torvalds 	pdev = chip->dev;
6471da177e4SLinus Torvalds 
6481da177e4SLinus Torvalds 	err = pnp_activate_dev(pdev);
6491da177e4SLinus Torvalds 	if (err < 0) {
6501da177e4SLinus Torvalds 		snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
6511da177e4SLinus Torvalds 		return err;
6521da177e4SLinus Torvalds 	}
6531da177e4SLinus Torvalds 
6541da177e4SLinus Torvalds #ifdef OPTi93X
6551da177e4SLinus Torvalds 	port = pnp_port_start(pdev, 0) - 4;
6561ea73412STakashi Iwai 	fm_port = pnp_port_start(pdev, 1) + 8;
6571da177e4SLinus Torvalds #else
6581da177e4SLinus Torvalds 	if (pid->driver_data != 0x0924)
6591da177e4SLinus Torvalds 		port = pnp_port_start(pdev, 1);
6601ea73412STakashi Iwai 	fm_port = pnp_port_start(pdev, 2) + 8;
6611da177e4SLinus Torvalds #endif	/* OPTi93X */
6621da177e4SLinus Torvalds 	irq = pnp_irq(pdev, 0);
6631da177e4SLinus Torvalds 	dma1 = pnp_dma(pdev, 0);
6641da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
6651da177e4SLinus Torvalds 	dma2 = pnp_dma(pdev, 1);
6661da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
6671da177e4SLinus Torvalds 
6681da177e4SLinus Torvalds 	pdev = chip->devmpu;
6691da177e4SLinus Torvalds 	if (pdev && mpu_port > 0) {
6701da177e4SLinus Torvalds 		err = pnp_activate_dev(pdev);
6711da177e4SLinus Torvalds 		if (err < 0) {
6721da177e4SLinus Torvalds 			snd_printk(KERN_ERR "AUDIO pnp configure failure\n");
6731da177e4SLinus Torvalds 			mpu_port = -1;
6741da177e4SLinus Torvalds 			chip->devmpu = NULL;
6751da177e4SLinus Torvalds 		} else {
6761da177e4SLinus Torvalds 			mpu_port = pnp_port_start(pdev, 0);
6771da177e4SLinus Torvalds 			mpu_irq = pnp_irq(pdev, 0);
6781da177e4SLinus Torvalds 		}
6791da177e4SLinus Torvalds 	}
6801da177e4SLinus Torvalds 	return pid->driver_data;
6811da177e4SLinus Torvalds }
6821da177e4SLinus Torvalds #endif	/* CONFIG_PNP */
6831da177e4SLinus Torvalds 
684346c7a68STakashi Iwai static void snd_card_opti9xx_free(struct snd_card *card)
6851da177e4SLinus Torvalds {
68699a0b768STakashi Iwai 	struct snd_opti9xx *chip = card->private_data;
6871da177e4SLinus Torvalds 
6889f240a55SKrzysztof Helt 	if (chip) {
6899f240a55SKrzysztof Helt #ifdef OPTi93X
6907779f75fSKrzysztof Helt 		struct snd_wss *codec = chip->codec;
69182af6bc0STakashi Iwai 		if (codec && codec->irq > 0) {
6929f240a55SKrzysztof Helt 			disable_irq(codec->irq);
6939f240a55SKrzysztof Helt 			free_irq(codec->irq, codec);
6949f240a55SKrzysztof Helt 		}
6959f240a55SKrzysztof Helt #endif
696b1d5776dSTakashi Iwai 		release_and_free_resource(chip->res_mc_base);
6971da177e4SLinus Torvalds 	}
6989f240a55SKrzysztof Helt }
6991da177e4SLinus Torvalds 
7005e24c1c1STakashi Iwai static int __devinit snd_opti9xx_probe(struct snd_card *card)
7011da177e4SLinus Torvalds {
7021da177e4SLinus Torvalds 	static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
7031da177e4SLinus Torvalds 	int error;
70499a0b768STakashi Iwai 	struct snd_opti9xx *chip = card->private_data;
7057779f75fSKrzysztof Helt 	struct snd_wss *codec;
7069f240a55SKrzysztof Helt #ifdef CS4231
707346c7a68STakashi Iwai 	struct snd_timer *timer;
7089f240a55SKrzysztof Helt #endif
709346c7a68STakashi Iwai 	struct snd_pcm *pcm;
710346c7a68STakashi Iwai 	struct snd_rawmidi *rmidi;
711346c7a68STakashi Iwai 	struct snd_hwdep *synth;
7121da177e4SLinus Torvalds 
7131da177e4SLinus Torvalds 	if (! chip->res_mc_base &&
71499a0b768STakashi Iwai 	    (chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
71599a0b768STakashi Iwai 						"OPTi9xx MC")) == NULL)
7161da177e4SLinus Torvalds 		return -ENOMEM;
7171da177e4SLinus Torvalds 
7181da177e4SLinus Torvalds 	chip->wss_base = port;
7191da177e4SLinus Torvalds 	chip->fm_port = fm_port;
7201da177e4SLinus Torvalds 	chip->mpu_port = mpu_port;
7211da177e4SLinus Torvalds 	chip->irq = irq;
7221da177e4SLinus Torvalds 	chip->mpu_irq = mpu_irq;
7231da177e4SLinus Torvalds 	chip->dma1 = dma1;
7241da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
7251da177e4SLinus Torvalds 	chip->dma2 = dma2;
726a0d9274cSRene Herman #else
727a0d9274cSRene Herman 	chip->dma2 = -1;
7281da177e4SLinus Torvalds #endif
7291da177e4SLinus Torvalds 
7301da177e4SLinus Torvalds 	if (chip->wss_base == SNDRV_AUTO_PORT) {
7317779f75fSKrzysztof Helt 		chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4);
7327779f75fSKrzysztof Helt 		if (chip->wss_base < 0) {
733*4c9f1d3eSTakashi Iwai 			snd_printk(KERN_ERR "unable to find a free WSS port\n");
7341da177e4SLinus Torvalds 			return -EBUSY;
7351da177e4SLinus Torvalds 		}
7361da177e4SLinus Torvalds 	}
7377779f75fSKrzysztof Helt 	error = snd_opti9xx_configure(chip);
7387779f75fSKrzysztof Helt 	if (error)
7391da177e4SLinus Torvalds 		return error;
7401da177e4SLinus Torvalds 
7417779f75fSKrzysztof Helt 	error = snd_wss_create(card, chip->wss_base + 4, -1,
7421da177e4SLinus Torvalds 			       chip->irq, chip->dma1, chip->dma2,
743a0d9274cSRene Herman #ifdef OPTi93X
7447779f75fSKrzysztof Helt 			       WSS_HW_OPTI93X, WSS_HWSHARE_IRQ,
745a0d9274cSRene Herman #else
746a0d9274cSRene Herman 			       WSS_HW_DETECT, 0,
7479f240a55SKrzysztof Helt #endif
7487779f75fSKrzysztof Helt 			       &codec);
7497779f75fSKrzysztof Helt 	if (error < 0)
7501da177e4SLinus Torvalds 		return error;
7519f240a55SKrzysztof Helt #ifdef OPTi93X
7529f240a55SKrzysztof Helt 	chip->codec = codec;
7539f240a55SKrzysztof Helt #endif
754ead893c0SKrzysztof Helt 	error = snd_wss_pcm(codec, 0, &pcm);
7555664daa1SKrzysztof Helt 	if (error < 0)
7565664daa1SKrzysztof Helt 		return error;
7577779f75fSKrzysztof Helt 	error = snd_wss_mixer(codec);
7587779f75fSKrzysztof Helt 	if (error < 0)
7591da177e4SLinus Torvalds 		return error;
7609f240a55SKrzysztof Helt #ifdef CS4231
7617779f75fSKrzysztof Helt 	error = snd_wss_timer(codec, 0, &timer);
7627779f75fSKrzysztof Helt 	if (error < 0)
7631da177e4SLinus Torvalds 		return error;
7645664daa1SKrzysztof Helt #endif
7655664daa1SKrzysztof Helt #ifdef OPTi93X
7669f240a55SKrzysztof Helt 	error = request_irq(chip->irq, snd_opti93x_interrupt,
7679f240a55SKrzysztof Helt 			    IRQF_DISABLED, DEV_NAME" - WSS", codec);
7689f240a55SKrzysztof Helt 	if (error < 0) {
7699f240a55SKrzysztof Helt 		snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq);
7709f240a55SKrzysztof Helt 		return error;
7719f240a55SKrzysztof Helt 	}
7729f240a55SKrzysztof Helt #endif
7731da177e4SLinus Torvalds 	strcpy(card->driver, chip->name);
7741da177e4SLinus Torvalds 	sprintf(card->shortname, "OPTi %s", card->driver);
7751da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
7761da177e4SLinus Torvalds 	sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
7771da177e4SLinus Torvalds 		card->shortname, pcm->name, chip->wss_base + 4,
7781da177e4SLinus Torvalds 		chip->irq, chip->dma1, chip->dma2);
7791da177e4SLinus Torvalds #else
7801da177e4SLinus Torvalds 	sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
7811da177e4SLinus Torvalds 		card->shortname, pcm->name, chip->wss_base + 4,
7821da177e4SLinus Torvalds 		chip->irq, chip->dma1);
7831da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
7841da177e4SLinus Torvalds 
7851da177e4SLinus Torvalds 	if (chip->mpu_port <= 0 || chip->mpu_port == SNDRV_AUTO_PORT)
7861da177e4SLinus Torvalds 		rmidi = NULL;
7871da177e4SLinus Torvalds 	else
7881da177e4SLinus Torvalds 		if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
78965ca68b3SThomas Gleixner 				chip->mpu_port, 0, chip->mpu_irq, IRQF_DISABLED,
7901da177e4SLinus Torvalds 				&rmidi)))
79199a0b768STakashi Iwai 			snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
79299a0b768STakashi Iwai 				   chip->mpu_port);
7931da177e4SLinus Torvalds 
7941da177e4SLinus Torvalds 	if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) {
795346c7a68STakashi Iwai 		struct snd_opl3 *opl3 = NULL;
7961da177e4SLinus Torvalds #ifndef OPTi93X
7971da177e4SLinus Torvalds 		if (chip->hardware == OPTi9XX_HW_82C928 ||
7981da177e4SLinus Torvalds 		    chip->hardware == OPTi9XX_HW_82C929 ||
7991da177e4SLinus Torvalds 		    chip->hardware == OPTi9XX_HW_82C924) {
800346c7a68STakashi Iwai 			struct snd_opl4 *opl4;
8011da177e4SLinus Torvalds 			/* assume we have an OPL4 */
8021da177e4SLinus Torvalds 			snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
8031da177e4SLinus Torvalds 					       0x20, 0x20);
8041da177e4SLinus Torvalds 			if (snd_opl4_create(card,
8051da177e4SLinus Torvalds 					    chip->fm_port,
8061da177e4SLinus Torvalds 					    chip->fm_port - 8,
8071da177e4SLinus Torvalds 					    2, &opl3, &opl4) < 0) {
8081da177e4SLinus Torvalds 				/* no luck, use OPL3 instead */
8091da177e4SLinus Torvalds 				snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
8101da177e4SLinus Torvalds 						       0x00, 0x20);
8111da177e4SLinus Torvalds 			}
8121da177e4SLinus Torvalds 		}
8131da177e4SLinus Torvalds #endif	/* !OPTi93X */
8141da177e4SLinus Torvalds 		if (!opl3 && snd_opl3_create(card,
8151da177e4SLinus Torvalds 					     chip->fm_port,
8161da177e4SLinus Torvalds 					     chip->fm_port + 2,
8171da177e4SLinus Torvalds 					     OPL3_HW_AUTO, 0, &opl3) < 0) {
81899a0b768STakashi Iwai 			snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
8191da177e4SLinus Torvalds 				   chip->fm_port, chip->fm_port + 4 - 1);
8201da177e4SLinus Torvalds 		}
8211da177e4SLinus Torvalds 		if (opl3) {
822aa9c293aSKrzysztof Helt 			error = snd_opl3_hwdep_new(opl3, 0, 1, &synth);
823aa9c293aSKrzysztof Helt 			if (error < 0)
8241da177e4SLinus Torvalds 				return error;
8251da177e4SLinus Torvalds 		}
8261da177e4SLinus Torvalds 	}
8271da177e4SLinus Torvalds 
82899a0b768STakashi Iwai 	return snd_card_register(card);
82999a0b768STakashi Iwai }
83099a0b768STakashi Iwai 
83199a0b768STakashi Iwai static struct snd_card *snd_opti9xx_card_new(void)
83299a0b768STakashi Iwai {
83399a0b768STakashi Iwai 	struct snd_card *card;
83499a0b768STakashi Iwai 
83599a0b768STakashi Iwai 	card = snd_card_new(index, id, THIS_MODULE, sizeof(struct snd_opti9xx));
83699a0b768STakashi Iwai 	if (! card)
83799a0b768STakashi Iwai 		return NULL;
83899a0b768STakashi Iwai 	card->private_free = snd_card_opti9xx_free;
83999a0b768STakashi Iwai 	return card;
84099a0b768STakashi Iwai }
84199a0b768STakashi Iwai 
8425e24c1c1STakashi Iwai static int __devinit snd_opti9xx_isa_match(struct device *devptr,
8435e24c1c1STakashi Iwai 					   unsigned int dev)
8445e24c1c1STakashi Iwai {
845101f6f4bSTakashi Iwai #ifdef CONFIG_PNP
8465e24c1c1STakashi Iwai 	if (snd_opti9xx_pnp_is_probed)
8475e24c1c1STakashi Iwai 		return 0;
8485e24c1c1STakashi Iwai 	if (isapnp)
8495e24c1c1STakashi Iwai 		return 0;
850101f6f4bSTakashi Iwai #endif
8515e24c1c1STakashi Iwai 	return 1;
8525e24c1c1STakashi Iwai }
8535e24c1c1STakashi Iwai 
8545e24c1c1STakashi Iwai static int __devinit snd_opti9xx_isa_probe(struct device *devptr,
8555e24c1c1STakashi Iwai 					   unsigned int dev)
85699a0b768STakashi Iwai {
85799a0b768STakashi Iwai 	struct snd_card *card;
85899a0b768STakashi Iwai 	int error;
85999a0b768STakashi Iwai 	static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1};
86099a0b768STakashi Iwai #ifdef OPTi93X
86199a0b768STakashi Iwai 	static int possible_irqs[] = {5, 9, 10, 11, 7, -1};
86299a0b768STakashi Iwai #else
86399a0b768STakashi Iwai 	static int possible_irqs[] = {9, 10, 11, 7, -1};
86499a0b768STakashi Iwai #endif	/* OPTi93X */
86599a0b768STakashi Iwai 	static int possible_mpu_irqs[] = {5, 9, 10, 7, -1};
86699a0b768STakashi Iwai 	static int possible_dma1s[] = {3, 1, 0, -1};
86799a0b768STakashi Iwai #if defined(CS4231) || defined(OPTi93X)
86899a0b768STakashi Iwai 	static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
86999a0b768STakashi Iwai #endif	/* CS4231 || OPTi93X */
87099a0b768STakashi Iwai 
87199a0b768STakashi Iwai 	if (mpu_port == SNDRV_AUTO_PORT) {
87299a0b768STakashi Iwai 		if ((mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) {
87399a0b768STakashi Iwai 			snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
87499a0b768STakashi Iwai 			return -EBUSY;
87599a0b768STakashi Iwai 		}
87699a0b768STakashi Iwai 	}
87799a0b768STakashi Iwai 	if (irq == SNDRV_AUTO_IRQ) {
87899a0b768STakashi Iwai 		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
87999a0b768STakashi Iwai 			snd_printk(KERN_ERR "unable to find a free IRQ\n");
88099a0b768STakashi Iwai 			return -EBUSY;
88199a0b768STakashi Iwai 		}
88299a0b768STakashi Iwai 	}
88399a0b768STakashi Iwai 	if (mpu_irq == SNDRV_AUTO_IRQ) {
88499a0b768STakashi Iwai 		if ((mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {
88599a0b768STakashi Iwai 			snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
88699a0b768STakashi Iwai 			return -EBUSY;
88799a0b768STakashi Iwai 		}
88899a0b768STakashi Iwai 	}
88999a0b768STakashi Iwai 	if (dma1 == SNDRV_AUTO_DMA) {
89099a0b768STakashi Iwai 		if ((dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {
89199a0b768STakashi Iwai 			snd_printk(KERN_ERR "unable to find a free DMA1\n");
89299a0b768STakashi Iwai 			return -EBUSY;
89399a0b768STakashi Iwai 		}
89499a0b768STakashi Iwai 	}
89599a0b768STakashi Iwai #if defined(CS4231) || defined(OPTi93X)
89699a0b768STakashi Iwai 	if (dma2 == SNDRV_AUTO_DMA) {
89799a0b768STakashi Iwai 		if ((dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4])) < 0) {
898*4c9f1d3eSTakashi Iwai 			snd_printk(KERN_ERR "unable to find a free DMA2\n");
89999a0b768STakashi Iwai 			return -EBUSY;
90099a0b768STakashi Iwai 		}
90199a0b768STakashi Iwai 	}
90299a0b768STakashi Iwai #endif
90399a0b768STakashi Iwai 
90499a0b768STakashi Iwai 	card = snd_opti9xx_card_new();
90599a0b768STakashi Iwai 	if (! card)
90699a0b768STakashi Iwai 		return -ENOMEM;
90799a0b768STakashi Iwai 
90899a0b768STakashi Iwai 	if ((error = snd_card_opti9xx_detect(card, card->private_data)) < 0) {
9091da177e4SLinus Torvalds 		snd_card_free(card);
9101da177e4SLinus Torvalds 		return error;
9111da177e4SLinus Torvalds 	}
9125e24c1c1STakashi Iwai 	snd_card_set_dev(card, devptr);
91399a0b768STakashi Iwai 	if ((error = snd_opti9xx_probe(card)) < 0) {
91499a0b768STakashi Iwai 		snd_card_free(card);
91599a0b768STakashi Iwai 		return error;
91699a0b768STakashi Iwai 	}
9175e24c1c1STakashi Iwai 	dev_set_drvdata(devptr, card);
9181da177e4SLinus Torvalds 	return 0;
9191da177e4SLinus Torvalds }
9201da177e4SLinus Torvalds 
9215e24c1c1STakashi Iwai static int __devexit snd_opti9xx_isa_remove(struct device *devptr,
9225e24c1c1STakashi Iwai 					    unsigned int dev)
92399a0b768STakashi Iwai {
9245e24c1c1STakashi Iwai 	snd_card_free(dev_get_drvdata(devptr));
9255e24c1c1STakashi Iwai 	dev_set_drvdata(devptr, NULL);
92699a0b768STakashi Iwai 	return 0;
92799a0b768STakashi Iwai }
92899a0b768STakashi Iwai 
9295e24c1c1STakashi Iwai static struct isa_driver snd_opti9xx_driver = {
9305e24c1c1STakashi Iwai 	.match		= snd_opti9xx_isa_match,
9315e24c1c1STakashi Iwai 	.probe		= snd_opti9xx_isa_probe,
9325e24c1c1STakashi Iwai 	.remove		= __devexit_p(snd_opti9xx_isa_remove),
93399a0b768STakashi Iwai 	/* FIXME: suspend/resume */
93499a0b768STakashi Iwai 	.driver		= {
93583c51c0aSRene Herman 		.name	= DEV_NAME
93699a0b768STakashi Iwai 	},
93799a0b768STakashi Iwai };
93899a0b768STakashi Iwai 
9391da177e4SLinus Torvalds #ifdef CONFIG_PNP
9405e24c1c1STakashi Iwai static int __devinit snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
94199a0b768STakashi Iwai 					   const struct pnp_card_device_id *pid)
94299a0b768STakashi Iwai {
94399a0b768STakashi Iwai 	struct snd_card *card;
94499a0b768STakashi Iwai 	int error, hw;
94599a0b768STakashi Iwai 	struct snd_opti9xx *chip;
94699a0b768STakashi Iwai 
94799a0b768STakashi Iwai 	if (snd_opti9xx_pnp_is_probed)
94899a0b768STakashi Iwai 		return -EBUSY;
94999a0b768STakashi Iwai 	if (! isapnp)
95099a0b768STakashi Iwai 		return -ENODEV;
95199a0b768STakashi Iwai 	card = snd_opti9xx_card_new();
95299a0b768STakashi Iwai 	if (! card)
95399a0b768STakashi Iwai 		return -ENOMEM;
95499a0b768STakashi Iwai 	chip = card->private_data;
95599a0b768STakashi Iwai 
95699a0b768STakashi Iwai 	hw = snd_card_opti9xx_pnp(chip, pcard, pid);
95799a0b768STakashi Iwai 	switch (hw) {
95899a0b768STakashi Iwai 	case 0x0924:
95999a0b768STakashi Iwai 		hw = OPTi9XX_HW_82C924;
96099a0b768STakashi Iwai 		break;
96199a0b768STakashi Iwai 	case 0x0925:
96299a0b768STakashi Iwai 		hw = OPTi9XX_HW_82C925;
96399a0b768STakashi Iwai 		break;
96499a0b768STakashi Iwai 	case 0x0931:
96599a0b768STakashi Iwai 		hw = OPTi9XX_HW_82C931;
96699a0b768STakashi Iwai 		break;
96799a0b768STakashi Iwai 	default:
96899a0b768STakashi Iwai 		snd_card_free(card);
96999a0b768STakashi Iwai 		return -ENODEV;
97099a0b768STakashi Iwai 	}
97199a0b768STakashi Iwai 
97299a0b768STakashi Iwai 	if ((error = snd_opti9xx_init(chip, hw))) {
97399a0b768STakashi Iwai 		snd_card_free(card);
97499a0b768STakashi Iwai 		return error;
97599a0b768STakashi Iwai 	}
97699a0b768STakashi Iwai 	if (hw <= OPTi9XX_HW_82C930)
97799a0b768STakashi Iwai 		chip->mc_base -= 0x80;
97899a0b768STakashi Iwai 	snd_card_set_dev(card, &pcard->card->dev);
97999a0b768STakashi Iwai 	if ((error = snd_opti9xx_probe(card)) < 0) {
98099a0b768STakashi Iwai 		snd_card_free(card);
98199a0b768STakashi Iwai 		return error;
98299a0b768STakashi Iwai 	}
98399a0b768STakashi Iwai 	pnp_set_card_drvdata(pcard, card);
98499a0b768STakashi Iwai 	snd_opti9xx_pnp_is_probed = 1;
98599a0b768STakashi Iwai 	return 0;
98699a0b768STakashi Iwai }
98799a0b768STakashi Iwai 
9881da177e4SLinus Torvalds static void __devexit snd_opti9xx_pnp_remove(struct pnp_card_link * pcard)
9891da177e4SLinus Torvalds {
99099a0b768STakashi Iwai 	snd_card_free(pnp_get_card_drvdata(pcard));
99199a0b768STakashi Iwai 	pnp_set_card_drvdata(pcard, NULL);
99299a0b768STakashi Iwai 	snd_opti9xx_pnp_is_probed = 0;
9931da177e4SLinus Torvalds }
9941da177e4SLinus Torvalds 
9951da177e4SLinus Torvalds static struct pnp_card_driver opti9xx_pnpc_driver = {
9961da177e4SLinus Torvalds 	.flags		= PNP_DRIVER_RES_DISABLE,
9971da177e4SLinus Torvalds 	.name		= "opti9xx",
9981da177e4SLinus Torvalds 	.id_table	= snd_opti9xx_pnpids,
99999a0b768STakashi Iwai 	.probe		= snd_opti9xx_pnp_probe,
10001da177e4SLinus Torvalds 	.remove		= __devexit_p(snd_opti9xx_pnp_remove),
10011da177e4SLinus Torvalds };
10021da177e4SLinus Torvalds #endif
10031da177e4SLinus Torvalds 
100499a0b768STakashi Iwai #ifdef OPTi93X
100599a0b768STakashi Iwai #define CHIP_NAME	"82C93x"
100699a0b768STakashi Iwai #else
100799a0b768STakashi Iwai #define CHIP_NAME	"82C92x"
100899a0b768STakashi Iwai #endif
100999a0b768STakashi Iwai 
10101da177e4SLinus Torvalds static int __init alsa_card_opti9xx_init(void)
10111da177e4SLinus Torvalds {
10120bbbc4caSTakashi Iwai #ifdef CONFIG_PNP
101399a0b768STakashi Iwai 	pnp_register_card_driver(&opti9xx_pnpc_driver);
101499a0b768STakashi Iwai 	if (snd_opti9xx_pnp_is_probed)
10151da177e4SLinus Torvalds 		return 0;
1016101f6f4bSTakashi Iwai 	pnp_unregister_card_driver(&opti9xx_pnpc_driver);
10170bbbc4caSTakashi Iwai #endif
10185e24c1c1STakashi Iwai 	return isa_register_driver(&snd_opti9xx_driver, 1);
10191da177e4SLinus Torvalds }
10201da177e4SLinus Torvalds 
10211da177e4SLinus Torvalds static void __exit alsa_card_opti9xx_exit(void)
10221da177e4SLinus Torvalds {
1023f7a9275dSClemens Ladisch 	if (!snd_opti9xx_pnp_is_probed) {
10245e24c1c1STakashi Iwai 		isa_unregister_driver(&snd_opti9xx_driver);
10255e24c1c1STakashi Iwai 		return;
1026f7a9275dSClemens Ladisch 	}
10270bbbc4caSTakashi Iwai #ifdef CONFIG_PNP
10281da177e4SLinus Torvalds 	pnp_unregister_card_driver(&opti9xx_pnpc_driver);
10290bbbc4caSTakashi Iwai #endif
10301da177e4SLinus Torvalds }
10311da177e4SLinus Torvalds 
10321da177e4SLinus Torvalds module_init(alsa_card_opti9xx_init)
10331da177e4SLinus Torvalds module_exit(alsa_card_opti9xx_exit)
1034