xref: /openbmc/linux/sound/isa/opl3sa2.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  Driver for Yamaha OPL3-SA[2,3] soundcards
4c1017a4cSJaroslav Kysela  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
51da177e4SLinus Torvalds  */
61da177e4SLinus Torvalds 
71da177e4SLinus Torvalds #include <linux/init.h>
8704e0520STakashi Iwai #include <linux/err.h>
95e24c1c1STakashi Iwai #include <linux/isa.h>
101da177e4SLinus Torvalds #include <linux/interrupt.h>
111da177e4SLinus Torvalds #include <linux/pm.h>
121da177e4SLinus Torvalds #include <linux/pnp.h>
1365a77217SPaul Gortmaker #include <linux/module.h>
146cbbfe1cSTakashi Iwai #include <linux/io.h>
151da177e4SLinus Torvalds #include <sound/core.h>
1661ef19d7SKrzysztof Helt #include <sound/wss.h>
171da177e4SLinus Torvalds #include <sound/mpu401.h>
181da177e4SLinus Torvalds #include <sound/opl3.h>
191da177e4SLinus Torvalds #include <sound/initval.h>
20a1c7a7d8STakashi Iwai #include <sound/tlv.h>
211da177e4SLinus Torvalds 
22c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
231da177e4SLinus Torvalds MODULE_DESCRIPTION("Yamaha OPL3SA2+");
241da177e4SLinus Torvalds MODULE_LICENSE("GPL");
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
271da177e4SLinus Torvalds static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
28a67ff6a5SRusty Russell static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
291da177e4SLinus Torvalds #ifdef CONFIG_PNP
30a67ff6a5SRusty Russell static bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
311da177e4SLinus Torvalds #endif
321da177e4SLinus Torvalds static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0xf86,0x370,0x100 */
331da177e4SLinus Torvalds static long sb_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260 */
341da177e4SLinus Torvalds static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x530,0xe80,0xf40,0x604 */
351da177e4SLinus Torvalds static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x388 */
361da177e4SLinus Torvalds static long midi_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;/* 0x330,0x300 */
371da177e4SLinus Torvalds static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 0,1,3,5,9,11,12,15 */
381da177e4SLinus Torvalds static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
391da177e4SLinus Torvalds static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 1,3,5,6,7 */
406581f4e7STakashi Iwai static int opl3sa3_ymode[SNDRV_CARDS];   /* 0,1,2,3 */ /*SL Added*/
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds module_param_array(index, int, NULL, 0444);
431da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for OPL3-SA soundcard.");
441da177e4SLinus Torvalds module_param_array(id, charp, NULL, 0444);
451da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for OPL3-SA soundcard.");
461da177e4SLinus Torvalds module_param_array(enable, bool, NULL, 0444);
471da177e4SLinus Torvalds MODULE_PARM_DESC(enable, "Enable OPL3-SA soundcard.");
481da177e4SLinus Torvalds #ifdef CONFIG_PNP
491da177e4SLinus Torvalds module_param_array(isapnp, bool, NULL, 0444);
501da177e4SLinus Torvalds MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
511da177e4SLinus Torvalds #endif
52e992ef57SDavid Howells module_param_hw_array(port, long, ioport, NULL, 0444);
531da177e4SLinus Torvalds MODULE_PARM_DESC(port, "Port # for OPL3-SA driver.");
54e992ef57SDavid Howells module_param_hw_array(sb_port, long, ioport, NULL, 0444);
551da177e4SLinus Torvalds MODULE_PARM_DESC(sb_port, "SB port # for OPL3-SA driver.");
56e992ef57SDavid Howells module_param_hw_array(wss_port, long, ioport, NULL, 0444);
571da177e4SLinus Torvalds MODULE_PARM_DESC(wss_port, "WSS port # for OPL3-SA driver.");
58e992ef57SDavid Howells module_param_hw_array(fm_port, long, ioport, NULL, 0444);
591da177e4SLinus Torvalds MODULE_PARM_DESC(fm_port, "FM port # for OPL3-SA driver.");
60e992ef57SDavid Howells module_param_hw_array(midi_port, long, ioport, NULL, 0444);
611da177e4SLinus Torvalds MODULE_PARM_DESC(midi_port, "MIDI port # for OPL3-SA driver.");
62e992ef57SDavid Howells module_param_hw_array(irq, int, irq, NULL, 0444);
631da177e4SLinus Torvalds MODULE_PARM_DESC(irq, "IRQ # for OPL3-SA driver.");
64e992ef57SDavid Howells module_param_hw_array(dma1, int, dma, NULL, 0444);
651da177e4SLinus Torvalds MODULE_PARM_DESC(dma1, "DMA1 # for OPL3-SA driver.");
66e992ef57SDavid Howells module_param_hw_array(dma2, int, dma, NULL, 0444);
671da177e4SLinus Torvalds MODULE_PARM_DESC(dma2, "DMA2 # for OPL3-SA driver.");
681da177e4SLinus Torvalds module_param_array(opl3sa3_ymode, int, NULL, 0444);
691da177e4SLinus Torvalds MODULE_PARM_DESC(opl3sa3_ymode, "Speaker size selection for 3D Enhancement mode: Desktop/Large Notebook/Small Notebook/HiFi.");
701da177e4SLinus Torvalds 
7159b1b34fSTakashi Iwai #ifdef CONFIG_PNP
72609d7694SRene Herman static int isa_registered;
73f7a9275dSClemens Ladisch static int pnp_registered;
74f7a9275dSClemens Ladisch static int pnpc_registered;
7559b1b34fSTakashi Iwai #endif
76f7a9275dSClemens Ladisch 
771da177e4SLinus Torvalds /* control ports */
781da177e4SLinus Torvalds #define OPL3SA2_PM_CTRL		0x01
791da177e4SLinus Torvalds #define OPL3SA2_SYS_CTRL		0x02
801da177e4SLinus Torvalds #define OPL3SA2_IRQ_CONFIG	0x03
811da177e4SLinus Torvalds #define OPL3SA2_IRQ_STATUS	0x04
821da177e4SLinus Torvalds #define OPL3SA2_DMA_CONFIG	0x06
831da177e4SLinus Torvalds #define OPL3SA2_MASTER_LEFT	0x07
841da177e4SLinus Torvalds #define OPL3SA2_MASTER_RIGHT	0x08
851da177e4SLinus Torvalds #define OPL3SA2_MIC		0x09
861da177e4SLinus Torvalds #define OPL3SA2_MISC		0x0A
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds /* opl3sa3 only */
891da177e4SLinus Torvalds #define OPL3SA3_DGTL_DOWN	0x12
901da177e4SLinus Torvalds #define OPL3SA3_ANLG_DOWN	0x13
911da177e4SLinus Torvalds #define OPL3SA3_WIDE		0x14
921da177e4SLinus Torvalds #define OPL3SA3_BASS		0x15
931da177e4SLinus Torvalds #define OPL3SA3_TREBLE		0x16
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds /* power management bits */
961da177e4SLinus Torvalds #define OPL3SA2_PM_ADOWN		0x20
971da177e4SLinus Torvalds #define OPL3SA2_PM_PSV		0x04
981da177e4SLinus Torvalds #define OPL3SA2_PM_PDN		0x02
991da177e4SLinus Torvalds #define OPL3SA2_PM_PDX		0x01
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds #define OPL3SA2_PM_D0	0x00
1021da177e4SLinus Torvalds #define OPL3SA2_PM_D3	(OPL3SA2_PM_ADOWN|OPL3SA2_PM_PSV|OPL3SA2_PM_PDN|OPL3SA2_PM_PDX)
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds struct snd_opl3sa2 {
1051da177e4SLinus Torvalds 	int version;		/* 2 or 3 */
1061da177e4SLinus Torvalds 	unsigned long port;	/* control port */
1071da177e4SLinus Torvalds 	struct resource *res_port; /* control port resource */
1081da177e4SLinus Torvalds 	int irq;
1091da177e4SLinus Torvalds 	int single_dma;
1101da177e4SLinus Torvalds 	spinlock_t reg_lock;
111ec6c5ae3STakashi Iwai 	struct snd_hwdep *synth;
112ec6c5ae3STakashi Iwai 	struct snd_rawmidi *rmidi;
1137779f75fSKrzysztof Helt 	struct snd_wss *wss;
1141da177e4SLinus Torvalds 	unsigned char ctlregs[0x20];
1151da177e4SLinus Torvalds 	int ymode;		/* SL added */
116ec6c5ae3STakashi Iwai 	struct snd_kcontrol *master_switch;
117ec6c5ae3STakashi Iwai 	struct snd_kcontrol *master_volume;
1181da177e4SLinus Torvalds };
1191da177e4SLinus Torvalds 
12043bcd973STakashi Iwai #define PFX	"opl3sa2: "
12143bcd973STakashi Iwai 
1221da177e4SLinus Torvalds #ifdef CONFIG_PNP
1231da177e4SLinus Torvalds 
124607b5d7fSArvind Yadav static const struct pnp_device_id snd_opl3sa2_pnpbiosids[] = {
12553b26663SJaroslav Kysela 	{ .id = "YMH0021" },
1261265509cSJaroslav Kysela 	{ .id = "NMX2210" },	/* Gateway Solo 2500 */
1271265509cSJaroslav Kysela 	{ .id = "" }		/* end */
1281265509cSJaroslav Kysela };
1291265509cSJaroslav Kysela 
1301265509cSJaroslav Kysela MODULE_DEVICE_TABLE(pnp, snd_opl3sa2_pnpbiosids);
1311265509cSJaroslav Kysela 
132607b5d7fSArvind Yadav static const struct pnp_card_device_id snd_opl3sa2_pnpids[] = {
1331da177e4SLinus Torvalds 	/* Yamaha YMF719E-S (Genius Sound Maker 3DX) */
1341da177e4SLinus Torvalds 	{ .id = "YMH0020", .devs = { { "YMH0021" } } },
1351da177e4SLinus Torvalds 	/* Yamaha OPL3-SA3 (integrated on Intel's Pentium II AL440LX motherboard) */
1361da177e4SLinus Torvalds 	{ .id = "YMH0030", .devs = { { "YMH0021" } } },
1371da177e4SLinus Torvalds 	/* Yamaha OPL3-SA2 */
1381da177e4SLinus Torvalds 	{ .id = "YMH0800", .devs = { { "YMH0021" } } },
1391da177e4SLinus Torvalds 	/* Yamaha OPL3-SA2 */
1401da177e4SLinus Torvalds 	{ .id = "YMH0801", .devs = { { "YMH0021" } } },
1411da177e4SLinus Torvalds 	/* NeoMagic MagicWave 3DX */
1421da177e4SLinus Torvalds 	{ .id = "NMX2200", .devs = { { "YMH2210" } } },
1434700418cSPaul Vojta 	/* NeoMagic MagicWave 3D */
1444700418cSPaul Vojta 	{ .id = "NMX2200", .devs = { { "NMX2210" } } },
1451da177e4SLinus Torvalds 	/* --- */
1461da177e4SLinus Torvalds 	{ .id = "" }	/* end */
1471da177e4SLinus Torvalds };
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pnp_card, snd_opl3sa2_pnpids);
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds #endif /* CONFIG_PNP */
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds /* read control port (w/o spinlock) */
__snd_opl3sa2_read(struct snd_opl3sa2 * chip,unsigned char reg)155ec6c5ae3STakashi Iwai static unsigned char __snd_opl3sa2_read(struct snd_opl3sa2 *chip, unsigned char reg)
1561da177e4SLinus Torvalds {
1571da177e4SLinus Torvalds 	unsigned char result;
1581da177e4SLinus Torvalds #if 0
1591da177e4SLinus Torvalds 	outb(0x1d, port);	/* password */
1604c9f1d3eSTakashi Iwai 	printk(KERN_DEBUG "read [0x%lx] = 0x%x\n", port, inb(port));
1611da177e4SLinus Torvalds #endif
1621da177e4SLinus Torvalds 	outb(reg, chip->port);	/* register */
1631da177e4SLinus Torvalds 	result = inb(chip->port + 1);
1641da177e4SLinus Torvalds #if 0
1654c9f1d3eSTakashi Iwai 	printk(KERN_DEBUG "read [0x%lx] = 0x%x [0x%x]\n",
1664c9f1d3eSTakashi Iwai 	       port, result, inb(port));
1671da177e4SLinus Torvalds #endif
1681da177e4SLinus Torvalds 	return result;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds /* read control port (with spinlock) */
snd_opl3sa2_read(struct snd_opl3sa2 * chip,unsigned char reg)172ec6c5ae3STakashi Iwai static unsigned char snd_opl3sa2_read(struct snd_opl3sa2 *chip, unsigned char reg)
1731da177e4SLinus Torvalds {
1741da177e4SLinus Torvalds 	unsigned long flags;
1751da177e4SLinus Torvalds 	unsigned char result;
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
1781da177e4SLinus Torvalds 	result = __snd_opl3sa2_read(chip, reg);
1791da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
1801da177e4SLinus Torvalds 	return result;
1811da177e4SLinus Torvalds }
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds /* write control port (w/o spinlock) */
__snd_opl3sa2_write(struct snd_opl3sa2 * chip,unsigned char reg,unsigned char value)184ec6c5ae3STakashi Iwai static void __snd_opl3sa2_write(struct snd_opl3sa2 *chip, unsigned char reg, unsigned char value)
1851da177e4SLinus Torvalds {
1861da177e4SLinus Torvalds #if 0
1871da177e4SLinus Torvalds 	outb(0x1d, port);	/* password */
1881da177e4SLinus Torvalds #endif
1891da177e4SLinus Torvalds 	outb(reg, chip->port);	/* register */
1901da177e4SLinus Torvalds 	outb(value, chip->port + 1);
1911da177e4SLinus Torvalds 	chip->ctlregs[reg] = value;
1921da177e4SLinus Torvalds }
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds /* write control port (with spinlock) */
snd_opl3sa2_write(struct snd_opl3sa2 * chip,unsigned char reg,unsigned char value)195ec6c5ae3STakashi Iwai static void snd_opl3sa2_write(struct snd_opl3sa2 *chip, unsigned char reg, unsigned char value)
1961da177e4SLinus Torvalds {
1971da177e4SLinus Torvalds 	unsigned long flags;
1981da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
1991da177e4SLinus Torvalds 	__snd_opl3sa2_write(chip, reg, value);
2001da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
2011da177e4SLinus Torvalds }
2021da177e4SLinus Torvalds 
snd_opl3sa2_detect(struct snd_card * card)2031bff292eSBill Pemberton static int snd_opl3sa2_detect(struct snd_card *card)
2041da177e4SLinus Torvalds {
205503c10d8SKrzysztof Helt 	struct snd_opl3sa2 *chip = card->private_data;
2061da177e4SLinus Torvalds 	unsigned long port;
2071da177e4SLinus Torvalds 	unsigned char tmp, tmp1;
2081da177e4SLinus Torvalds 	char str[2];
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds 	port = chip->port;
211*6bf39b5dSTakashi Iwai 	chip->res_port = devm_request_region(card->dev, port, 2,
212*6bf39b5dSTakashi Iwai 					     "OPL3-SA control");
2134b3bd108STakashi Iwai 	if (!chip->res_port) {
21443bcd973STakashi Iwai 		snd_printk(KERN_ERR PFX "can't grab port 0x%lx\n", port);
2151da177e4SLinus Torvalds 		return -EBUSY;
2161da177e4SLinus Torvalds 	}
2174c9f1d3eSTakashi Iwai 	/*
2184c9f1d3eSTakashi Iwai 	snd_printk(KERN_DEBUG "REG 0A = 0x%x\n",
2194c9f1d3eSTakashi Iwai 		   snd_opl3sa2_read(chip, 0x0a));
2204c9f1d3eSTakashi Iwai 	*/
2211da177e4SLinus Torvalds 	chip->version = 0;
2221da177e4SLinus Torvalds 	tmp = snd_opl3sa2_read(chip, OPL3SA2_MISC);
2231da177e4SLinus Torvalds 	if (tmp == 0xff) {
2241da177e4SLinus Torvalds 		snd_printd("OPL3-SA [0x%lx] detect = 0x%x\n", port, tmp);
2251da177e4SLinus Torvalds 		return -ENODEV;
2261da177e4SLinus Torvalds 	}
2271da177e4SLinus Torvalds 	switch (tmp & 0x07) {
2281da177e4SLinus Torvalds 	case 0x01:
2291da177e4SLinus Torvalds 		chip->version = 2; /* YMF711 */
2301da177e4SLinus Torvalds 		break;
2311da177e4SLinus Torvalds 	default:
2321da177e4SLinus Torvalds 		chip->version = 3;
2331da177e4SLinus Torvalds 		/* 0x02 - standard */
2341da177e4SLinus Torvalds 		/* 0x03 - YM715B */
2351da177e4SLinus Torvalds 		/* 0x04 - YM719 - OPL-SA4? */
2361da177e4SLinus Torvalds 		/* 0x05 - OPL3-SA3 - Libretto 100 */
237e2340465SPaul Vojta 		/* 0x07 - unknown - Neomagic MagicWave 3D */
2381da177e4SLinus Torvalds 		break;
2391da177e4SLinus Torvalds 	}
2401da177e4SLinus Torvalds 	str[0] = chip->version + '0';
2411da177e4SLinus Torvalds 	str[1] = 0;
2421da177e4SLinus Torvalds 	strcat(card->shortname, str);
2431da177e4SLinus Torvalds 	snd_opl3sa2_write(chip, OPL3SA2_MISC, tmp ^ 7);
2444b3bd108STakashi Iwai 	tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MISC);
2454b3bd108STakashi Iwai 	if (tmp1 != tmp) {
2461da177e4SLinus Torvalds 		snd_printd("OPL3-SA [0x%lx] detect (1) = 0x%x (0x%x)\n", port, tmp, tmp1);
2471da177e4SLinus Torvalds 		return -ENODEV;
2481da177e4SLinus Torvalds 	}
249b595076aSUwe Kleine-König 	/* try if the MIC register is accessible */
2501da177e4SLinus Torvalds 	tmp = snd_opl3sa2_read(chip, OPL3SA2_MIC);
2511da177e4SLinus Torvalds 	snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x8a);
2524b3bd108STakashi Iwai 	tmp1 = snd_opl3sa2_read(chip, OPL3SA2_MIC);
2534b3bd108STakashi Iwai 	if ((tmp1 & 0x9f) != 0x8a) {
2541da177e4SLinus Torvalds 		snd_printd("OPL3-SA [0x%lx] detect (2) = 0x%x (0x%x)\n", port, tmp, tmp1);
2551da177e4SLinus Torvalds 		return -ENODEV;
2561da177e4SLinus Torvalds 	}
2571da177e4SLinus Torvalds 	snd_opl3sa2_write(chip, OPL3SA2_MIC, 0x9f);
2581da177e4SLinus Torvalds 	/* initialization */
2591da177e4SLinus Torvalds 	/* Power Management - full on */
2601da177e4SLinus Torvalds 	snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D0);
2611da177e4SLinus Torvalds 	if (chip->version > 2) {
2621da177e4SLinus Torvalds 		/* ymode is bits 4&5 (of 0 to 7) on all but opl3sa2 versions */
2631da177e4SLinus Torvalds 		snd_opl3sa2_write(chip, OPL3SA2_SYS_CTRL, (chip->ymode << 4));
2641da177e4SLinus Torvalds 	} else {
2651da177e4SLinus Torvalds 		/* default for opl3sa2 versions */
2661da177e4SLinus Torvalds 		snd_opl3sa2_write(chip, OPL3SA2_SYS_CTRL, 0x00);
2671da177e4SLinus Torvalds 	}
2681da177e4SLinus Torvalds 	snd_opl3sa2_write(chip, OPL3SA2_IRQ_CONFIG, 0x0d);	/* Interrupt Channel Configuration - IRQ A = OPL3 + MPU + WSS */
2691da177e4SLinus Torvalds 	if (chip->single_dma) {
2701da177e4SLinus Torvalds 		snd_opl3sa2_write(chip, OPL3SA2_DMA_CONFIG, 0x03);	/* DMA Configuration - DMA A = WSS-R + WSS-P */
2711da177e4SLinus Torvalds 	} else {
2721da177e4SLinus Torvalds 		snd_opl3sa2_write(chip, OPL3SA2_DMA_CONFIG, 0x21);	/* DMA Configuration - DMA B = WSS-R, DMA A = WSS-P */
2731da177e4SLinus Torvalds 	}
2741da177e4SLinus Torvalds 	snd_opl3sa2_write(chip, OPL3SA2_MISC, 0x80 | (tmp & 7));	/* Miscellaneous - default */
2751da177e4SLinus Torvalds 	if (chip->version > 2) {
2761da177e4SLinus Torvalds 		snd_opl3sa2_write(chip, OPL3SA3_DGTL_DOWN, 0x00);	/* Digital Block Partial Power Down - default */
2771da177e4SLinus Torvalds 		snd_opl3sa2_write(chip, OPL3SA3_ANLG_DOWN, 0x00);	/* Analog Block Partial Power Down - default */
2781da177e4SLinus Torvalds 	}
2791da177e4SLinus Torvalds 	return 0;
2801da177e4SLinus Torvalds }
2811da177e4SLinus Torvalds 
snd_opl3sa2_interrupt(int irq,void * dev_id)2827d12e780SDavid Howells static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id)
2831da177e4SLinus Torvalds {
2841da177e4SLinus Torvalds 	unsigned short status;
285503c10d8SKrzysztof Helt 	struct snd_card *card = dev_id;
286503c10d8SKrzysztof Helt 	struct snd_opl3sa2 *chip;
2871da177e4SLinus Torvalds 	int handled = 0;
2881da177e4SLinus Torvalds 
289f87426e0SKrzysztof Helt 	if (card == NULL)
2901da177e4SLinus Torvalds 		return IRQ_NONE;
2911da177e4SLinus Torvalds 
292503c10d8SKrzysztof Helt 	chip = card->private_data;
2931da177e4SLinus Torvalds 	status = snd_opl3sa2_read(chip, OPL3SA2_IRQ_STATUS);
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds 	if (status & 0x20) {
2961da177e4SLinus Torvalds 		handled = 1;
2971da177e4SLinus Torvalds 		snd_opl3_interrupt(chip->synth);
2981da177e4SLinus Torvalds 	}
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds 	if ((status & 0x10) && chip->rmidi != NULL) {
3011da177e4SLinus Torvalds 		handled = 1;
3027d12e780SDavid Howells 		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
3031da177e4SLinus Torvalds 	}
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds 	if (status & 0x07) {	/* TI,CI,PI */
3061da177e4SLinus Torvalds 		handled = 1;
3077779f75fSKrzysztof Helt 		snd_wss_interrupt(irq, chip->wss);
3081da177e4SLinus Torvalds 	}
3091da177e4SLinus Torvalds 
3101da177e4SLinus Torvalds 	if (status & 0x40) { /* hardware volume change */
3111da177e4SLinus Torvalds 		handled = 1;
3121da177e4SLinus Torvalds 		/* reading from Master Lch register at 0x07 clears this bit */
3131da177e4SLinus Torvalds 		snd_opl3sa2_read(chip, OPL3SA2_MASTER_RIGHT);
3141da177e4SLinus Torvalds 		snd_opl3sa2_read(chip, OPL3SA2_MASTER_LEFT);
3151da177e4SLinus Torvalds 		if (chip->master_switch && chip->master_volume) {
316503c10d8SKrzysztof Helt 			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
317503c10d8SKrzysztof Helt 					&chip->master_switch->id);
318503c10d8SKrzysztof Helt 			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
319503c10d8SKrzysztof Helt 					&chip->master_volume->id);
3201da177e4SLinus Torvalds 		}
3211da177e4SLinus Torvalds 	}
3221da177e4SLinus Torvalds 	return IRQ_RETVAL(handled);
3231da177e4SLinus Torvalds }
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds #define OPL3SA2_SINGLE(xname, xindex, reg, shift, mask, invert) \
3261da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
327f87426e0SKrzysztof Helt   .info = snd_wss_info_single, \
3281da177e4SLinus Torvalds   .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \
3291da177e4SLinus Torvalds   .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
330a1c7a7d8STakashi Iwai #define OPL3SA2_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
331a1c7a7d8STakashi Iwai { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
332a1c7a7d8STakashi Iwai   .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
333a1c7a7d8STakashi Iwai   .name = xname, .index = xindex, \
334f87426e0SKrzysztof Helt   .info = snd_wss_info_single, \
335a1c7a7d8STakashi Iwai   .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \
336a1c7a7d8STakashi Iwai   .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
337a1c7a7d8STakashi Iwai   .tlv = { .p = (xtlv) } }
3381da177e4SLinus Torvalds 
snd_opl3sa2_get_single(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)339ec6c5ae3STakashi Iwai static int snd_opl3sa2_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
3401da177e4SLinus Torvalds {
341ec6c5ae3STakashi Iwai 	struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
3421da177e4SLinus Torvalds 	unsigned long flags;
3431da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
3441da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 8) & 0xff;
3451da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 16) & 0xff;
3461da177e4SLinus Torvalds 	int invert = (kcontrol->private_value >> 24) & 0xff;
3471da177e4SLinus Torvalds 
3481da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
3491da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = (chip->ctlregs[reg] >> shift) & mask;
3501da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
3511da177e4SLinus Torvalds 	if (invert)
3521da177e4SLinus Torvalds 		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
3531da177e4SLinus Torvalds 	return 0;
3541da177e4SLinus Torvalds }
3551da177e4SLinus Torvalds 
snd_opl3sa2_put_single(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)356ec6c5ae3STakashi Iwai static int snd_opl3sa2_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
3571da177e4SLinus Torvalds {
358ec6c5ae3STakashi Iwai 	struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
3591da177e4SLinus Torvalds 	unsigned long flags;
3601da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
3611da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 8) & 0xff;
3621da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 16) & 0xff;
3631da177e4SLinus Torvalds 	int invert = (kcontrol->private_value >> 24) & 0xff;
3641da177e4SLinus Torvalds 	int change;
3651da177e4SLinus Torvalds 	unsigned short val, oval;
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds 	val = (ucontrol->value.integer.value[0] & mask);
3681da177e4SLinus Torvalds 	if (invert)
3691da177e4SLinus Torvalds 		val = mask - val;
3701da177e4SLinus Torvalds 	val <<= shift;
3711da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
3721da177e4SLinus Torvalds 	oval = chip->ctlregs[reg];
3731da177e4SLinus Torvalds 	val = (oval & ~(mask << shift)) | val;
3741da177e4SLinus Torvalds 	change = val != oval;
3751da177e4SLinus Torvalds 	__snd_opl3sa2_write(chip, reg, val);
3761da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
3771da177e4SLinus Torvalds 	return change;
3781da177e4SLinus Torvalds }
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds #define OPL3SA2_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
3811da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
382f87426e0SKrzysztof Helt   .info = snd_wss_info_double, \
3831da177e4SLinus Torvalds   .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \
3841da177e4SLinus Torvalds   .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
385a1c7a7d8STakashi Iwai #define OPL3SA2_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \
386a1c7a7d8STakashi Iwai { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
387a1c7a7d8STakashi Iwai   .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
388a1c7a7d8STakashi Iwai   .name = xname, .index = xindex, \
389f87426e0SKrzysztof Helt   .info = snd_wss_info_double, \
390a1c7a7d8STakashi Iwai   .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \
391a1c7a7d8STakashi Iwai   .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22), \
392a1c7a7d8STakashi Iwai   .tlv = { .p = (xtlv) } }
3931da177e4SLinus Torvalds 
snd_opl3sa2_get_double(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)394ec6c5ae3STakashi Iwai static int snd_opl3sa2_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
3951da177e4SLinus Torvalds {
396ec6c5ae3STakashi Iwai 	struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
3971da177e4SLinus Torvalds 	unsigned long flags;
3981da177e4SLinus Torvalds 	int left_reg = kcontrol->private_value & 0xff;
3991da177e4SLinus Torvalds 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
4001da177e4SLinus Torvalds 	int shift_left = (kcontrol->private_value >> 16) & 0x07;
4011da177e4SLinus Torvalds 	int shift_right = (kcontrol->private_value >> 19) & 0x07;
4021da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
4031da177e4SLinus Torvalds 	int invert = (kcontrol->private_value >> 22) & 1;
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
4061da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = (chip->ctlregs[left_reg] >> shift_left) & mask;
4071da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = (chip->ctlregs[right_reg] >> shift_right) & mask;
4081da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
4091da177e4SLinus Torvalds 	if (invert) {
4101da177e4SLinus Torvalds 		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
4111da177e4SLinus Torvalds 		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
4121da177e4SLinus Torvalds 	}
4131da177e4SLinus Torvalds 	return 0;
4141da177e4SLinus Torvalds }
4151da177e4SLinus Torvalds 
snd_opl3sa2_put_double(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)416ec6c5ae3STakashi Iwai static int snd_opl3sa2_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
4171da177e4SLinus Torvalds {
418ec6c5ae3STakashi Iwai 	struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
4191da177e4SLinus Torvalds 	unsigned long flags;
4201da177e4SLinus Torvalds 	int left_reg = kcontrol->private_value & 0xff;
4211da177e4SLinus Torvalds 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
4221da177e4SLinus Torvalds 	int shift_left = (kcontrol->private_value >> 16) & 0x07;
4231da177e4SLinus Torvalds 	int shift_right = (kcontrol->private_value >> 19) & 0x07;
4241da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
4251da177e4SLinus Torvalds 	int invert = (kcontrol->private_value >> 22) & 1;
4261da177e4SLinus Torvalds 	int change;
4271da177e4SLinus Torvalds 	unsigned short val1, val2, oval1, oval2;
4281da177e4SLinus Torvalds 
4291da177e4SLinus Torvalds 	val1 = ucontrol->value.integer.value[0] & mask;
4301da177e4SLinus Torvalds 	val2 = ucontrol->value.integer.value[1] & mask;
4311da177e4SLinus Torvalds 	if (invert) {
4321da177e4SLinus Torvalds 		val1 = mask - val1;
4331da177e4SLinus Torvalds 		val2 = mask - val2;
4341da177e4SLinus Torvalds 	}
4351da177e4SLinus Torvalds 	val1 <<= shift_left;
4361da177e4SLinus Torvalds 	val2 <<= shift_right;
4371da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->reg_lock, flags);
4381da177e4SLinus Torvalds 	if (left_reg != right_reg) {
4391da177e4SLinus Torvalds 		oval1 = chip->ctlregs[left_reg];
4401da177e4SLinus Torvalds 		oval2 = chip->ctlregs[right_reg];
4411da177e4SLinus Torvalds 		val1 = (oval1 & ~(mask << shift_left)) | val1;
4421da177e4SLinus Torvalds 		val2 = (oval2 & ~(mask << shift_right)) | val2;
4431da177e4SLinus Torvalds 		change = val1 != oval1 || val2 != oval2;
4441da177e4SLinus Torvalds 		__snd_opl3sa2_write(chip, left_reg, val1);
4451da177e4SLinus Torvalds 		__snd_opl3sa2_write(chip, right_reg, val2);
4461da177e4SLinus Torvalds 	} else {
4471da177e4SLinus Torvalds 		oval1 = chip->ctlregs[left_reg];
4481da177e4SLinus Torvalds 		val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
4491da177e4SLinus Torvalds 		change = val1 != oval1;
4501da177e4SLinus Torvalds 		__snd_opl3sa2_write(chip, left_reg, val1);
4511da177e4SLinus Torvalds 	}
4521da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->reg_lock, flags);
4531da177e4SLinus Torvalds 	return change;
4541da177e4SLinus Torvalds }
4551da177e4SLinus Torvalds 
4560cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(db_scale_master, -3000, 200, 0);
4570cb29ea0STakashi Iwai static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
458a1c7a7d8STakashi Iwai 
459fdd1f6fdSTakashi Iwai static const struct snd_kcontrol_new snd_opl3sa2_controls[] = {
4601da177e4SLinus Torvalds OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1),
461a1c7a7d8STakashi Iwai OPL3SA2_DOUBLE_TLV("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1,
462a1c7a7d8STakashi Iwai 		   db_scale_master),
4631da177e4SLinus Torvalds OPL3SA2_SINGLE("Mic Playback Switch", 0, 0x09, 7, 1, 1),
464a1c7a7d8STakashi Iwai OPL3SA2_SINGLE_TLV("Mic Playback Volume", 0, 0x09, 0, 31, 1,
465a1c7a7d8STakashi Iwai 		   db_scale_5bit_12db_max),
4661661c615SKrzysztof Helt OPL3SA2_SINGLE("ZV Port Switch", 0, 0x02, 0, 1, 0),
4671da177e4SLinus Torvalds };
4681da177e4SLinus Torvalds 
469fdd1f6fdSTakashi Iwai static const struct snd_kcontrol_new snd_opl3sa2_tone_controls[] = {
4701da177e4SLinus Torvalds OPL3SA2_DOUBLE("3D Control - Wide", 0, 0x14, 0x14, 4, 0, 7, 0),
4711da177e4SLinus Torvalds OPL3SA2_DOUBLE("Tone Control - Bass", 0, 0x15, 0x15, 4, 0, 7, 0),
4721da177e4SLinus Torvalds OPL3SA2_DOUBLE("Tone Control - Treble", 0, 0x16, 0x16, 4, 0, 7, 0)
4731da177e4SLinus Torvalds };
4741da177e4SLinus Torvalds 
snd_opl3sa2_master_free(struct snd_kcontrol * kcontrol)475ec6c5ae3STakashi Iwai static void snd_opl3sa2_master_free(struct snd_kcontrol *kcontrol)
4761da177e4SLinus Torvalds {
477ec6c5ae3STakashi Iwai 	struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol);
4781da177e4SLinus Torvalds 	chip->master_switch = NULL;
4791da177e4SLinus Torvalds 	chip->master_volume = NULL;
4801da177e4SLinus Torvalds }
4811da177e4SLinus Torvalds 
snd_opl3sa2_mixer(struct snd_card * card)4821bff292eSBill Pemberton static int snd_opl3sa2_mixer(struct snd_card *card)
4831da177e4SLinus Torvalds {
484503c10d8SKrzysztof Helt 	struct snd_opl3sa2 *chip = card->private_data;
485ec6c5ae3STakashi Iwai 	struct snd_ctl_elem_id id1, id2;
486ec6c5ae3STakashi Iwai 	struct snd_kcontrol *kctl;
4871da177e4SLinus Torvalds 	unsigned int idx;
4881da177e4SLinus Torvalds 	int err;
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 	memset(&id1, 0, sizeof(id1));
4911da177e4SLinus Torvalds 	memset(&id2, 0, sizeof(id2));
4921da177e4SLinus Torvalds 	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
4931da177e4SLinus Torvalds 	/* reassign AUX0 to CD */
4941da177e4SLinus Torvalds         strcpy(id1.name, "Aux Playback Switch");
4951da177e4SLinus Torvalds         strcpy(id2.name, "CD Playback Switch");
4964b3bd108STakashi Iwai 	err = snd_ctl_rename_id(card, &id1, &id2);
4974b3bd108STakashi Iwai 	if (err < 0) {
49873e77ba0STakashi Iwai 		snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
4991da177e4SLinus Torvalds                 return err;
50073e77ba0STakashi Iwai 	}
5011da177e4SLinus Torvalds         strcpy(id1.name, "Aux Playback Volume");
5021da177e4SLinus Torvalds         strcpy(id2.name, "CD Playback Volume");
5034b3bd108STakashi Iwai 	err = snd_ctl_rename_id(card, &id1, &id2);
5044b3bd108STakashi Iwai 	if (err < 0) {
50573e77ba0STakashi Iwai 		snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
5061da177e4SLinus Torvalds                 return err;
50773e77ba0STakashi Iwai 	}
5081da177e4SLinus Torvalds 	/* reassign AUX1 to FM */
5091da177e4SLinus Torvalds         strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
5101da177e4SLinus Torvalds         strcpy(id2.name, "FM Playback Switch");
5114b3bd108STakashi Iwai 	err = snd_ctl_rename_id(card, &id1, &id2);
5124b3bd108STakashi Iwai 	if (err < 0) {
51373e77ba0STakashi Iwai 		snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
5141da177e4SLinus Torvalds                 return err;
51573e77ba0STakashi Iwai 	}
5161da177e4SLinus Torvalds         strcpy(id1.name, "Aux Playback Volume");
5171da177e4SLinus Torvalds         strcpy(id2.name, "FM Playback Volume");
5184b3bd108STakashi Iwai 	err = snd_ctl_rename_id(card, &id1, &id2);
5194b3bd108STakashi Iwai 	if (err < 0) {
52073e77ba0STakashi Iwai 		snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n");
5211da177e4SLinus Torvalds                 return err;
52273e77ba0STakashi Iwai 	}
5231da177e4SLinus Torvalds 	/* add OPL3SA2 controls */
5241da177e4SLinus Torvalds 	for (idx = 0; idx < ARRAY_SIZE(snd_opl3sa2_controls); idx++) {
5254b3bd108STakashi Iwai 		kctl = snd_ctl_new1(&snd_opl3sa2_controls[idx], chip);
5264b3bd108STakashi Iwai 		err = snd_ctl_add(card, kctl);
5274b3bd108STakashi Iwai 		if (err < 0)
5281da177e4SLinus Torvalds 			return err;
5291da177e4SLinus Torvalds 		switch (idx) {
5301da177e4SLinus Torvalds 		case 0: chip->master_switch = kctl; kctl->private_free = snd_opl3sa2_master_free; break;
5311da177e4SLinus Torvalds 		case 1: chip->master_volume = kctl; kctl->private_free = snd_opl3sa2_master_free; break;
5321da177e4SLinus Torvalds 		}
5331da177e4SLinus Torvalds 	}
5341da177e4SLinus Torvalds 	if (chip->version > 2) {
5354b3bd108STakashi Iwai 		for (idx = 0; idx < ARRAY_SIZE(snd_opl3sa2_tone_controls); idx++) {
5364b3bd108STakashi Iwai 			err = snd_ctl_add(card, snd_ctl_new1(&snd_opl3sa2_tone_controls[idx], chip));
5374b3bd108STakashi Iwai 			if (err < 0)
5381da177e4SLinus Torvalds 				return err;
5391da177e4SLinus Torvalds 		}
5404b3bd108STakashi Iwai 	}
5411da177e4SLinus Torvalds 	return 0;
5421da177e4SLinus Torvalds }
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds /* Power Management support functions */
5451da177e4SLinus Torvalds #ifdef CONFIG_PM
snd_opl3sa2_suspend(struct snd_card * card,pm_message_t state)546ec6c5ae3STakashi Iwai static int snd_opl3sa2_suspend(struct snd_card *card, pm_message_t state)
5471da177e4SLinus Torvalds {
548dde332b6SKrzysztof Helt 	if (card) {
549704e0520STakashi Iwai 		struct snd_opl3sa2 *chip = card->private_data;
5501da177e4SLinus Torvalds 
551704e0520STakashi Iwai 		snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
5527779f75fSKrzysztof Helt 		chip->wss->suspend(chip->wss);
5531da177e4SLinus Torvalds 		/* power down */
5541da177e4SLinus Torvalds 		snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D3);
555dde332b6SKrzysztof Helt 	}
5561da177e4SLinus Torvalds 
5571da177e4SLinus Torvalds 	return 0;
5581da177e4SLinus Torvalds }
5591da177e4SLinus Torvalds 
snd_opl3sa2_resume(struct snd_card * card)560ec6c5ae3STakashi Iwai static int snd_opl3sa2_resume(struct snd_card *card)
5611da177e4SLinus Torvalds {
562dde332b6SKrzysztof Helt 	struct snd_opl3sa2 *chip;
5631da177e4SLinus Torvalds 	int i;
5641da177e4SLinus Torvalds 
565dde332b6SKrzysztof Helt 	if (!card)
566dde332b6SKrzysztof Helt 		return 0;
567dde332b6SKrzysztof Helt 
568dde332b6SKrzysztof Helt 	chip = card->private_data;
5691da177e4SLinus Torvalds 	/* power up */
5701da177e4SLinus Torvalds 	snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D0);
5711da177e4SLinus Torvalds 
5721da177e4SLinus Torvalds 	/* restore registers */
5731da177e4SLinus Torvalds 	for (i = 2; i <= 0x0a; i++) {
5741da177e4SLinus Torvalds 		if (i != OPL3SA2_IRQ_STATUS)
5751da177e4SLinus Torvalds 			snd_opl3sa2_write(chip, i, chip->ctlregs[i]);
5761da177e4SLinus Torvalds 	}
5771da177e4SLinus Torvalds 	if (chip->version > 2) {
5781da177e4SLinus Torvalds 		for (i = 0x12; i <= 0x16; i++)
5791da177e4SLinus Torvalds 			snd_opl3sa2_write(chip, i, chip->ctlregs[i]);
5801da177e4SLinus Torvalds 	}
5817779f75fSKrzysztof Helt 	/* restore wss */
5827779f75fSKrzysztof Helt 	chip->wss->resume(chip->wss);
5831da177e4SLinus Torvalds 
584704e0520STakashi Iwai 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
5851da177e4SLinus Torvalds 	return 0;
5861da177e4SLinus Torvalds }
5871da177e4SLinus Torvalds #endif /* CONFIG_PM */
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds #ifdef CONFIG_PNP
snd_opl3sa2_pnp(int dev,struct snd_opl3sa2 * chip,struct pnp_dev * pdev)5901bff292eSBill Pemberton static int snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip,
591704e0520STakashi Iwai 			   struct pnp_dev *pdev)
5921da177e4SLinus Torvalds {
593109c53f8SRene Herman 	if (pnp_activate_dev(pdev) < 0) {
594109c53f8SRene Herman 		snd_printk(KERN_ERR "PnP configure failure (out of resources?)\n");
5951da177e4SLinus Torvalds 		return -EBUSY;
5961da177e4SLinus Torvalds 	}
5971da177e4SLinus Torvalds 	sb_port[dev] = pnp_port_start(pdev, 0);
5981da177e4SLinus Torvalds 	wss_port[dev] = pnp_port_start(pdev, 1);
5991da177e4SLinus Torvalds 	fm_port[dev] = pnp_port_start(pdev, 2);
6001da177e4SLinus Torvalds 	midi_port[dev] = pnp_port_start(pdev, 3);
6011da177e4SLinus Torvalds 	port[dev] = pnp_port_start(pdev, 4);
6021da177e4SLinus Torvalds 	dma1[dev] = pnp_dma(pdev, 0);
6031da177e4SLinus Torvalds 	dma2[dev] = pnp_dma(pdev, 1);
6041da177e4SLinus Torvalds 	irq[dev] = pnp_irq(pdev, 0);
6051265509cSJaroslav Kysela 	snd_printdd("%sPnP OPL3-SA: sb port=0x%lx, wss port=0x%lx, fm port=0x%lx, midi port=0x%lx\n",
6061265509cSJaroslav Kysela 		pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", sb_port[dev], wss_port[dev], fm_port[dev], midi_port[dev]);
6071265509cSJaroslav Kysela 	snd_printdd("%sPnP OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n",
6081265509cSJaroslav Kysela 		pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", port[dev], dma1[dev], dma2[dev], irq[dev]);
6091da177e4SLinus Torvalds 	return 0;
6101da177e4SLinus Torvalds }
6111da177e4SLinus Torvalds #endif /* CONFIG_PNP */
6121da177e4SLinus Torvalds 
snd_opl3sa2_card_new(struct device * pdev,int dev,struct snd_card ** cardp)6134323cc4dSTakashi Iwai static int snd_opl3sa2_card_new(struct device *pdev, int dev,
6144323cc4dSTakashi Iwai 				struct snd_card **cardp)
6151da177e4SLinus Torvalds {
616704e0520STakashi Iwai 	struct snd_card *card;
617704e0520STakashi Iwai 	struct snd_opl3sa2 *chip;
618c95eadd2STakashi Iwai 	int err;
619704e0520STakashi Iwai 
620*6bf39b5dSTakashi Iwai 	err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
621c95eadd2STakashi Iwai 				sizeof(struct snd_opl3sa2), &card);
622c95eadd2STakashi Iwai 	if (err < 0)
623c95eadd2STakashi Iwai 		return err;
624704e0520STakashi Iwai 	strcpy(card->driver, "OPL3SA2");
6251713c0d5SKrzysztof Helt 	strcpy(card->shortname, "Yamaha OPL3-SA");
626704e0520STakashi Iwai 	chip = card->private_data;
627704e0520STakashi Iwai 	spin_lock_init(&chip->reg_lock);
628704e0520STakashi Iwai 	chip->irq = -1;
629c95eadd2STakashi Iwai 	*cardp = card;
630c95eadd2STakashi Iwai 	return 0;
6311da177e4SLinus Torvalds }
6321da177e4SLinus Torvalds 
snd_opl3sa2_probe(struct snd_card * card,int dev)6331bff292eSBill Pemberton static int snd_opl3sa2_probe(struct snd_card *card, int dev)
6341da177e4SLinus Torvalds {
6351da177e4SLinus Torvalds 	int xirq, xdma1, xdma2;
6361da177e4SLinus Torvalds 	struct snd_opl3sa2 *chip;
6377779f75fSKrzysztof Helt 	struct snd_wss *wss;
638ec6c5ae3STakashi Iwai 	struct snd_opl3 *opl3;
6391da177e4SLinus Torvalds 	int err;
6401da177e4SLinus Torvalds 
641704e0520STakashi Iwai 	/* initialise this card from supplied (or default) parameter*/
642704e0520STakashi Iwai 	chip = card->private_data;
643704e0520STakashi Iwai 	chip->ymode = opl3sa3_ymode[dev] & 0x03 ;
644704e0520STakashi Iwai 	chip->port = port[dev];
645704e0520STakashi Iwai 	xirq = irq[dev];
646704e0520STakashi Iwai 	xdma1 = dma1[dev];
647704e0520STakashi Iwai 	xdma2 = dma2[dev];
648704e0520STakashi Iwai 	if (xdma2 < 0)
649704e0520STakashi Iwai 		chip->single_dma = 1;
650503c10d8SKrzysztof Helt 	err = snd_opl3sa2_detect(card);
651503c10d8SKrzysztof Helt 	if (err < 0)
652704e0520STakashi Iwai 		return err;
653*6bf39b5dSTakashi Iwai 	err = devm_request_irq(card->dev, xirq, snd_opl3sa2_interrupt, 0,
654503c10d8SKrzysztof Helt 			       "OPL3-SA2", card);
655503c10d8SKrzysztof Helt 	if (err) {
656704e0520STakashi Iwai 		snd_printk(KERN_ERR PFX "can't grab IRQ %d\n", xirq);
657704e0520STakashi Iwai 		return -ENODEV;
658704e0520STakashi Iwai 	}
659704e0520STakashi Iwai 	chip->irq = xirq;
660d3e163bfSTakashi Iwai 	card->sync_irq = chip->irq;
6617779f75fSKrzysztof Helt 	err = snd_wss_create(card,
662704e0520STakashi Iwai 			     wss_port[dev] + 4, -1,
663704e0520STakashi Iwai 			     xirq, xdma1, xdma2,
6647779f75fSKrzysztof Helt 			     WSS_HW_OPL3SA2, WSS_HWSHARE_IRQ, &wss);
6657779f75fSKrzysztof Helt 	if (err < 0) {
666704e0520STakashi Iwai 		snd_printd("Oops, WSS not detected at 0x%lx\n", wss_port[dev] + 4);
667704e0520STakashi Iwai 		return err;
668704e0520STakashi Iwai 	}
6697779f75fSKrzysztof Helt 	chip->wss = wss;
670fa60c065SLars-Peter Clausen 	err = snd_wss_pcm(wss, 0);
6717779f75fSKrzysztof Helt 	if (err < 0)
672704e0520STakashi Iwai 		return err;
6737779f75fSKrzysztof Helt 	err = snd_wss_mixer(wss);
6747779f75fSKrzysztof Helt 	if (err < 0)
675704e0520STakashi Iwai 		return err;
676503c10d8SKrzysztof Helt 	err = snd_opl3sa2_mixer(card);
677503c10d8SKrzysztof Helt 	if (err < 0)
678704e0520STakashi Iwai 		return err;
679fa60c065SLars-Peter Clausen 	err = snd_wss_timer(wss, 0);
6807779f75fSKrzysztof Helt 	if (err < 0)
681704e0520STakashi Iwai 		return err;
682704e0520STakashi Iwai 	if (fm_port[dev] >= 0x340 && fm_port[dev] < 0x400) {
6834b3bd108STakashi Iwai 		err = snd_opl3_create(card, fm_port[dev],
684704e0520STakashi Iwai 				      fm_port[dev] + 2,
6854b3bd108STakashi Iwai 				      OPL3_HW_OPL3, 0, &opl3);
6864b3bd108STakashi Iwai 		if (err < 0)
687704e0520STakashi Iwai 			return err;
6884b3bd108STakashi Iwai 		err = snd_opl3_timer_new(opl3, 1, 2);
6894b3bd108STakashi Iwai 		if (err < 0)
690704e0520STakashi Iwai 			return err;
6914b3bd108STakashi Iwai 		err = snd_opl3_hwdep_new(opl3, 0, 1, &chip->synth);
6924b3bd108STakashi Iwai 		if (err < 0)
693704e0520STakashi Iwai 			return err;
694704e0520STakashi Iwai 	}
695704e0520STakashi Iwai 	if (midi_port[dev] >= 0x300 && midi_port[dev] < 0x340) {
6964b3bd108STakashi Iwai 		err = snd_mpu401_uart_new(card, 0, MPU401_HW_OPL3SA2,
697dba8b469SClemens Ladisch 					  midi_port[dev],
698dba8b469SClemens Ladisch 					  MPU401_INFO_IRQ_HOOK, -1,
6994b3bd108STakashi Iwai 					  &chip->rmidi);
7004b3bd108STakashi Iwai 		if (err < 0)
701704e0520STakashi Iwai 			return err;
702704e0520STakashi Iwai 	}
703704e0520STakashi Iwai 	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
704704e0520STakashi Iwai 		card->shortname, chip->port, xirq, xdma1);
7051d79716aSAlexey Dobriyan 	if (xdma2 >= 0)
706704e0520STakashi Iwai 		sprintf(card->longname + strlen(card->longname), "&%d", xdma2);
707704e0520STakashi Iwai 
708704e0520STakashi Iwai 	return snd_card_register(card);
709704e0520STakashi Iwai }
710704e0520STakashi Iwai 
711704e0520STakashi Iwai #ifdef CONFIG_PNP
snd_opl3sa2_pnp_detect(struct pnp_dev * pdev,const struct pnp_device_id * id)7121bff292eSBill Pemberton static int snd_opl3sa2_pnp_detect(struct pnp_dev *pdev,
713704e0520STakashi Iwai 				  const struct pnp_device_id *id)
714704e0520STakashi Iwai {
715704e0520STakashi Iwai 	static int dev;
716704e0520STakashi Iwai 	int err;
717704e0520STakashi Iwai 	struct snd_card *card;
718704e0520STakashi Iwai 
719704e0520STakashi Iwai 	if (pnp_device_is_isapnp(pdev))
720704e0520STakashi Iwai 		return -ENOENT;	/* we have another procedure - card */
721704e0520STakashi Iwai 	for (; dev < SNDRV_CARDS; dev++) {
722704e0520STakashi Iwai 		if (enable[dev] && isapnp[dev])
723704e0520STakashi Iwai 			break;
724704e0520STakashi Iwai 	}
725704e0520STakashi Iwai 	if (dev >= SNDRV_CARDS)
726704e0520STakashi Iwai 		return -ENODEV;
727704e0520STakashi Iwai 
7284323cc4dSTakashi Iwai 	err = snd_opl3sa2_card_new(&pdev->dev, dev, &card);
729c95eadd2STakashi Iwai 	if (err < 0)
730c95eadd2STakashi Iwai 		return err;
7314b3bd108STakashi Iwai 	err = snd_opl3sa2_pnp(dev, card->private_data, pdev);
732*6bf39b5dSTakashi Iwai 	if (err < 0)
733704e0520STakashi Iwai 		return err;
7344b3bd108STakashi Iwai 	err = snd_opl3sa2_probe(card, dev);
735*6bf39b5dSTakashi Iwai 	if (err < 0)
736704e0520STakashi Iwai 		return err;
737704e0520STakashi Iwai 	pnp_set_drvdata(pdev, card);
738704e0520STakashi Iwai 	dev++;
739704e0520STakashi Iwai 	return 0;
740704e0520STakashi Iwai }
741704e0520STakashi Iwai 
742704e0520STakashi Iwai #ifdef CONFIG_PM
snd_opl3sa2_pnp_suspend(struct pnp_dev * pdev,pm_message_t state)743704e0520STakashi Iwai static int snd_opl3sa2_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
744704e0520STakashi Iwai {
745704e0520STakashi Iwai 	return snd_opl3sa2_suspend(pnp_get_drvdata(pdev), state);
746704e0520STakashi Iwai }
snd_opl3sa2_pnp_resume(struct pnp_dev * pdev)747704e0520STakashi Iwai static int snd_opl3sa2_pnp_resume(struct pnp_dev *pdev)
748704e0520STakashi Iwai {
749704e0520STakashi Iwai 	return snd_opl3sa2_resume(pnp_get_drvdata(pdev));
750704e0520STakashi Iwai }
751704e0520STakashi Iwai #endif
752704e0520STakashi Iwai 
753704e0520STakashi Iwai static struct pnp_driver opl3sa2_pnp_driver = {
75462af9905STakashi Iwai 	.name = "snd-opl3sa2-pnpbios",
755704e0520STakashi Iwai 	.id_table = snd_opl3sa2_pnpbiosids,
756704e0520STakashi Iwai 	.probe = snd_opl3sa2_pnp_detect,
757704e0520STakashi Iwai #ifdef CONFIG_PM
758704e0520STakashi Iwai 	.suspend = snd_opl3sa2_pnp_suspend,
759704e0520STakashi Iwai 	.resume = snd_opl3sa2_pnp_resume,
760704e0520STakashi Iwai #endif
761704e0520STakashi Iwai };
762704e0520STakashi Iwai 
snd_opl3sa2_pnp_cdetect(struct pnp_card_link * pcard,const struct pnp_card_device_id * id)7631bff292eSBill Pemberton static int snd_opl3sa2_pnp_cdetect(struct pnp_card_link *pcard,
764704e0520STakashi Iwai 				   const struct pnp_card_device_id *id)
765704e0520STakashi Iwai {
766704e0520STakashi Iwai 	static int dev;
767704e0520STakashi Iwai 	struct pnp_dev *pdev;
768704e0520STakashi Iwai 	int err;
769704e0520STakashi Iwai 	struct snd_card *card;
770704e0520STakashi Iwai 
771704e0520STakashi Iwai 	pdev = pnp_request_card_device(pcard, id->devs[0].id, NULL);
77273e77ba0STakashi Iwai 	if (pdev == NULL) {
77373e77ba0STakashi Iwai 		snd_printk(KERN_ERR PFX "can't get pnp device from id '%s'\n",
77473e77ba0STakashi Iwai 			   id->devs[0].id);
775704e0520STakashi Iwai 		return -EBUSY;
77673e77ba0STakashi Iwai 	}
777704e0520STakashi Iwai 	for (; dev < SNDRV_CARDS; dev++) {
778704e0520STakashi Iwai 		if (enable[dev] && isapnp[dev])
779704e0520STakashi Iwai 			break;
780704e0520STakashi Iwai 	}
781704e0520STakashi Iwai 	if (dev >= SNDRV_CARDS)
782704e0520STakashi Iwai 		return -ENODEV;
783704e0520STakashi Iwai 
7844323cc4dSTakashi Iwai 	err = snd_opl3sa2_card_new(&pdev->dev, dev, &card);
785c95eadd2STakashi Iwai 	if (err < 0)
786c95eadd2STakashi Iwai 		return err;
7874b3bd108STakashi Iwai 	err = snd_opl3sa2_pnp(dev, card->private_data, pdev);
788*6bf39b5dSTakashi Iwai 	if (err < 0)
789704e0520STakashi Iwai 		return err;
7904b3bd108STakashi Iwai 	err = snd_opl3sa2_probe(card, dev);
791*6bf39b5dSTakashi Iwai 	if (err < 0)
792704e0520STakashi Iwai 		return err;
793704e0520STakashi Iwai 	pnp_set_card_drvdata(pcard, card);
794704e0520STakashi Iwai 	dev++;
795704e0520STakashi Iwai 	return 0;
796704e0520STakashi Iwai }
797704e0520STakashi Iwai 
798704e0520STakashi Iwai #ifdef CONFIG_PM
snd_opl3sa2_pnp_csuspend(struct pnp_card_link * pcard,pm_message_t state)799704e0520STakashi Iwai static int snd_opl3sa2_pnp_csuspend(struct pnp_card_link *pcard, pm_message_t state)
800704e0520STakashi Iwai {
801704e0520STakashi Iwai 	return snd_opl3sa2_suspend(pnp_get_card_drvdata(pcard), state);
802704e0520STakashi Iwai }
snd_opl3sa2_pnp_cresume(struct pnp_card_link * pcard)803704e0520STakashi Iwai static int snd_opl3sa2_pnp_cresume(struct pnp_card_link *pcard)
804704e0520STakashi Iwai {
805704e0520STakashi Iwai 	return snd_opl3sa2_resume(pnp_get_card_drvdata(pcard));
806704e0520STakashi Iwai }
807704e0520STakashi Iwai #endif
808704e0520STakashi Iwai 
809704e0520STakashi Iwai static struct pnp_card_driver opl3sa2_pnpc_driver = {
810704e0520STakashi Iwai 	.flags = PNP_DRIVER_RES_DISABLE,
81162af9905STakashi Iwai 	.name = "snd-opl3sa2-cpnp",
812704e0520STakashi Iwai 	.id_table = snd_opl3sa2_pnpids,
813704e0520STakashi Iwai 	.probe = snd_opl3sa2_pnp_cdetect,
814704e0520STakashi Iwai #ifdef CONFIG_PM
815704e0520STakashi Iwai 	.suspend = snd_opl3sa2_pnp_csuspend,
816704e0520STakashi Iwai 	.resume = snd_opl3sa2_pnp_cresume,
817704e0520STakashi Iwai #endif
818704e0520STakashi Iwai };
819704e0520STakashi Iwai #endif /* CONFIG_PNP */
820704e0520STakashi Iwai 
snd_opl3sa2_isa_match(struct device * pdev,unsigned int dev)8211bff292eSBill Pemberton static int snd_opl3sa2_isa_match(struct device *pdev,
8225e24c1c1STakashi Iwai 				 unsigned int dev)
823704e0520STakashi Iwai {
8245e24c1c1STakashi Iwai 	if (!enable[dev])
8255e24c1c1STakashi Iwai 		return 0;
8265e24c1c1STakashi Iwai #ifdef CONFIG_PNP
8275e24c1c1STakashi Iwai 	if (isapnp[dev])
8285e24c1c1STakashi Iwai 		return 0;
8295e24c1c1STakashi Iwai #endif
8301da177e4SLinus Torvalds 	if (port[dev] == SNDRV_AUTO_PORT) {
83143bcd973STakashi Iwai 		snd_printk(KERN_ERR PFX "specify port\n");
8325e24c1c1STakashi Iwai 		return 0;
8331da177e4SLinus Torvalds 	}
8341da177e4SLinus Torvalds 	if (wss_port[dev] == SNDRV_AUTO_PORT) {
83543bcd973STakashi Iwai 		snd_printk(KERN_ERR PFX "specify wss_port\n");
8365e24c1c1STakashi Iwai 		return 0;
8371da177e4SLinus Torvalds 	}
8381da177e4SLinus Torvalds 	if (fm_port[dev] == SNDRV_AUTO_PORT) {
83943bcd973STakashi Iwai 		snd_printk(KERN_ERR PFX "specify fm_port\n");
8405e24c1c1STakashi Iwai 		return 0;
8411da177e4SLinus Torvalds 	}
8421da177e4SLinus Torvalds 	if (midi_port[dev] == SNDRV_AUTO_PORT) {
84343bcd973STakashi Iwai 		snd_printk(KERN_ERR PFX "specify midi_port\n");
8445e24c1c1STakashi Iwai 		return 0;
8451da177e4SLinus Torvalds 	}
8465e24c1c1STakashi Iwai 	return 1;
8475e24c1c1STakashi Iwai }
8485e24c1c1STakashi Iwai 
snd_opl3sa2_isa_probe(struct device * pdev,unsigned int dev)8491bff292eSBill Pemberton static int snd_opl3sa2_isa_probe(struct device *pdev,
8505e24c1c1STakashi Iwai 				 unsigned int dev)
8515e24c1c1STakashi Iwai {
8525e24c1c1STakashi Iwai 	struct snd_card *card;
8535e24c1c1STakashi Iwai 	int err;
85443bcd973STakashi Iwai 
8554323cc4dSTakashi Iwai 	err = snd_opl3sa2_card_new(pdev, dev, &card);
856c95eadd2STakashi Iwai 	if (err < 0)
857c95eadd2STakashi Iwai 		return err;
8584b3bd108STakashi Iwai 	err = snd_opl3sa2_probe(card, dev);
859*6bf39b5dSTakashi Iwai 	if (err < 0)
8601da177e4SLinus Torvalds 		return err;
8615e24c1c1STakashi Iwai 	dev_set_drvdata(pdev, card);
8621da177e4SLinus Torvalds 	return 0;
8631da177e4SLinus Torvalds }
8641da177e4SLinus Torvalds 
865704e0520STakashi Iwai #ifdef CONFIG_PM
snd_opl3sa2_isa_suspend(struct device * dev,unsigned int n,pm_message_t state)8665e24c1c1STakashi Iwai static int snd_opl3sa2_isa_suspend(struct device *dev, unsigned int n,
8675e24c1c1STakashi Iwai 				   pm_message_t state)
8681da177e4SLinus Torvalds {
8695e24c1c1STakashi Iwai 	return snd_opl3sa2_suspend(dev_get_drvdata(dev), state);
8701da177e4SLinus Torvalds }
8711da177e4SLinus Torvalds 
snd_opl3sa2_isa_resume(struct device * dev,unsigned int n)8725e24c1c1STakashi Iwai static int snd_opl3sa2_isa_resume(struct device *dev, unsigned int n)
873704e0520STakashi Iwai {
8745e24c1c1STakashi Iwai 	return snd_opl3sa2_resume(dev_get_drvdata(dev));
875704e0520STakashi Iwai }
876704e0520STakashi Iwai #endif
877704e0520STakashi Iwai 
87883c51c0aSRene Herman #define DEV_NAME "opl3sa2"
879704e0520STakashi Iwai 
8805e24c1c1STakashi Iwai static struct isa_driver snd_opl3sa2_isa_driver = {
8815e24c1c1STakashi Iwai 	.match		= snd_opl3sa2_isa_match,
8825e24c1c1STakashi Iwai 	.probe		= snd_opl3sa2_isa_probe,
883704e0520STakashi Iwai #ifdef CONFIG_PM
8845e24c1c1STakashi Iwai 	.suspend	= snd_opl3sa2_isa_suspend,
8855e24c1c1STakashi Iwai 	.resume		= snd_opl3sa2_isa_resume,
886704e0520STakashi Iwai #endif
887704e0520STakashi Iwai 	.driver		= {
88883c51c0aSRene Herman 		.name	= DEV_NAME
889704e0520STakashi Iwai 	},
8901da177e4SLinus Torvalds };
8911da177e4SLinus Torvalds 
alsa_card_opl3sa2_init(void)8921da177e4SLinus Torvalds static int __init alsa_card_opl3sa2_init(void)
8931da177e4SLinus Torvalds {
8945e24c1c1STakashi Iwai 	int err;
8951da177e4SLinus Torvalds 
8965e24c1c1STakashi Iwai 	err = isa_register_driver(&snd_opl3sa2_isa_driver, SNDRV_CARDS);
89759b1b34fSTakashi Iwai #ifdef CONFIG_PNP
898609d7694SRene Herman 	if (!err)
899609d7694SRene Herman 		isa_registered = 1;
900609d7694SRene Herman 
901704e0520STakashi Iwai 	err = pnp_register_driver(&opl3sa2_pnp_driver);
9028c59c4a2SBjorn Helgaas 	if (!err)
903f7a9275dSClemens Ladisch 		pnp_registered = 1;
904609d7694SRene Herman 
905704e0520STakashi Iwai 	err = pnp_register_card_driver(&opl3sa2_pnpc_driver);
9068c59c4a2SBjorn Helgaas 	if (!err)
907f7a9275dSClemens Ladisch 		pnpc_registered = 1;
908609d7694SRene Herman 
909609d7694SRene Herman 	if (isa_registered || pnp_registered)
910609d7694SRene Herman 		err = 0;
91159b1b34fSTakashi Iwai #endif
912609d7694SRene Herman 	return err;
9131da177e4SLinus Torvalds }
9141da177e4SLinus Torvalds 
alsa_card_opl3sa2_exit(void)9151da177e4SLinus Torvalds static void __exit alsa_card_opl3sa2_exit(void)
9161da177e4SLinus Torvalds {
9175e24c1c1STakashi Iwai #ifdef CONFIG_PNP
9185e24c1c1STakashi Iwai 	if (pnpc_registered)
9195e24c1c1STakashi Iwai 		pnp_unregister_card_driver(&opl3sa2_pnpc_driver);
9205e24c1c1STakashi Iwai 	if (pnp_registered)
9215e24c1c1STakashi Iwai 		pnp_unregister_driver(&opl3sa2_pnp_driver);
922609d7694SRene Herman 	if (isa_registered)
9235e24c1c1STakashi Iwai #endif
9245e24c1c1STakashi Iwai 		isa_unregister_driver(&snd_opl3sa2_isa_driver);
9251da177e4SLinus Torvalds }
9261da177e4SLinus Torvalds 
9271da177e4SLinus Torvalds module_init(alsa_card_opl3sa2_init)
9281da177e4SLinus Torvalds module_exit(alsa_card_opl3sa2_exit)
929