xref: /linux/sound/isa/opti9xx/opti92x-ad1848.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds     card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards.
41da177e4SLinus Torvalds     Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it>
51da177e4SLinus Torvalds 
61da177e4SLinus Torvalds     Part of this code was developed at the Italian Ministry of Air Defence,
71da177e4SLinus Torvalds     Sixth Division (oh, che pace ...), Rome.
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds     Thanks to Maria Grazia Pollarini, Salvatore Vassallo.
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds */
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #include <linux/init.h>
1599a0b768STakashi Iwai #include <linux/err.h>
165e24c1c1STakashi Iwai #include <linux/isa.h>
1799a0b768STakashi Iwai #include <linux/delay.h>
181da177e4SLinus Torvalds #include <linux/pnp.h>
1965a77217SPaul Gortmaker #include <linux/module.h>
206cbbfe1cSTakashi Iwai #include <linux/io.h>
2199a0b768STakashi Iwai #include <asm/dma.h>
221da177e4SLinus Torvalds #include <sound/core.h>
23b2e8d7daSKrzysztof Helt #include <sound/tlv.h>
2461ef19d7SKrzysztof Helt #include <sound/wss.h>
251da177e4SLinus Torvalds #include <sound/mpu401.h>
261da177e4SLinus Torvalds #include <sound/opl3.h>
271da177e4SLinus Torvalds #ifndef OPTi93X
281da177e4SLinus Torvalds #include <sound/opl4.h>
291da177e4SLinus Torvalds #endif
30988aec3dSOndrej Zary #define SNDRV_LEGACY_FIND_FREE_IOPORT
311da177e4SLinus Torvalds #define SNDRV_LEGACY_FIND_FREE_IRQ
321da177e4SLinus Torvalds #define SNDRV_LEGACY_FIND_FREE_DMA
331da177e4SLinus Torvalds #include <sound/initval.h>
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
361da177e4SLinus Torvalds MODULE_LICENSE("GPL");
371da177e4SLinus Torvalds #ifdef OPTi93X
381da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi93X");
391da177e4SLinus Torvalds #else	/* OPTi93X */
401da177e4SLinus Torvalds #ifdef CS4231
411da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi92X - CS4231");
421da177e4SLinus Torvalds #else	/* CS4231 */
431da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi92X - AD1848");
441da177e4SLinus Torvalds #endif	/* CS4231 */
451da177e4SLinus Torvalds #endif	/* OPTi93X */
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
481da177e4SLinus Torvalds static char *id = SNDRV_DEFAULT_STR1;		/* ID for this card */
49a67ff6a5SRusty Russell //static bool enable = SNDRV_DEFAULT_ENABLE1;	/* Enable this card */
5051f6baadSRene Herman #ifdef CONFIG_PNP
51c578ae00SRandy Dunlap static bool isapnp = true;			/* Enable ISA PnP detection */
5251f6baadSRene Herman #endif
531da177e4SLinus Torvalds static long port = SNDRV_DEFAULT_PORT1; 	/* 0x530,0xe80,0xf40,0x604 */
541da177e4SLinus Torvalds static long mpu_port = SNDRV_DEFAULT_PORT1;	/* 0x300,0x310,0x320,0x330 */
551da177e4SLinus Torvalds static long fm_port = SNDRV_DEFAULT_PORT1;	/* 0x388 */
561da177e4SLinus Torvalds static int irq = SNDRV_DEFAULT_IRQ1;		/* 5,7,9,10,11 */
571da177e4SLinus Torvalds static int mpu_irq = SNDRV_DEFAULT_IRQ1;	/* 5,7,9,10 */
581da177e4SLinus Torvalds static int dma1 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
591da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
601da177e4SLinus Torvalds static int dma2 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
611da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds module_param(index, int, 0444);
641da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for opti9xx based soundcard.");
651da177e4SLinus Torvalds module_param(id, charp, 0444);
661da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard.");
671da177e4SLinus Torvalds //module_param(enable, bool, 0444);
681da177e4SLinus Torvalds //MODULE_PARM_DESC(enable, "Enable opti9xx soundcard.");
6951f6baadSRene Herman #ifdef CONFIG_PNP
701da177e4SLinus Torvalds module_param(isapnp, bool, 0444);
711da177e4SLinus Torvalds MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
7251f6baadSRene Herman #endif
73e992ef57SDavid Howells module_param_hw(port, long, ioport, 0444);
741da177e4SLinus Torvalds MODULE_PARM_DESC(port, "WSS port # for opti9xx driver.");
75e992ef57SDavid Howells module_param_hw(mpu_port, long, ioport, 0444);
761da177e4SLinus Torvalds MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver.");
77e992ef57SDavid Howells module_param_hw(fm_port, long, ioport, 0444);
781da177e4SLinus Torvalds MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver.");
79e992ef57SDavid Howells module_param_hw(irq, int, irq, 0444);
801da177e4SLinus Torvalds MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver.");
81e992ef57SDavid Howells module_param_hw(mpu_irq, int, irq, 0444);
821da177e4SLinus Torvalds MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver.");
83e992ef57SDavid Howells module_param_hw(dma1, int, dma, 0444);
841da177e4SLinus Torvalds MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver.");
851da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
86e992ef57SDavid Howells module_param_hw(dma2, int, dma, 0444);
871da177e4SLinus Torvalds MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
881da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds #define OPTi9XX_HW_82C928	1
911da177e4SLinus Torvalds #define OPTi9XX_HW_82C929	2
921da177e4SLinus Torvalds #define OPTi9XX_HW_82C924	3
931da177e4SLinus Torvalds #define OPTi9XX_HW_82C925	4
941da177e4SLinus Torvalds #define OPTi9XX_HW_82C930	5
951da177e4SLinus Torvalds #define OPTi9XX_HW_82C931	6
961da177e4SLinus Torvalds #define OPTi9XX_HW_82C933	7
971da177e4SLinus Torvalds #define OPTi9XX_HW_LAST		OPTi9XX_HW_82C933
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds #define OPTi9XX_MC_REG(n)	n
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds #ifdef OPTi93X
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds #define OPTi93X_STATUS			0x02
1041da177e4SLinus Torvalds #define OPTi93X_PORT(chip, r)		((chip)->port + OPTi93X_##r)
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds #define OPTi93X_IRQ_PLAYBACK		0x04
1071da177e4SLinus Torvalds #define OPTi93X_IRQ_CAPTURE		0x08
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds #endif /* OPTi93X */
1101da177e4SLinus Torvalds 
111346c7a68STakashi Iwai struct snd_opti9xx {
112*40b15de3STakashi Iwai 	struct snd_card *card;
1131da177e4SLinus Torvalds 	unsigned short hardware;
1141da177e4SLinus Torvalds 	unsigned char password;
1151da177e4SLinus Torvalds 	char name[7];
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 	unsigned long mc_base;
1181da177e4SLinus Torvalds 	struct resource *res_mc_base;
1191da177e4SLinus Torvalds 	unsigned long mc_base_size;
1201da177e4SLinus Torvalds #ifdef OPTi93X
1211da177e4SLinus Torvalds 	unsigned long mc_indir_index;
122e6960e19SKrzysztof Helt 	struct resource *res_mc_indir;
1231da177e4SLinus Torvalds #endif	/* OPTi93X */
1245dd25072SOndrej Zary 	struct snd_wss *codec;
1251da177e4SLinus Torvalds 	unsigned long pwd_reg;
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds 	spinlock_t lock;
1281da177e4SLinus Torvalds 
129fd8d4735SKrzysztof Helt 	long wss_base;
1301da177e4SLinus Torvalds 	int irq;
1311da177e4SLinus Torvalds };
1321da177e4SLinus Torvalds 
13399a0b768STakashi Iwai static int snd_opti9xx_pnp_is_probed;
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds #ifdef CONFIG_PNP
1361da177e4SLinus Torvalds 
137529d2576SArvind Yadav static const struct pnp_card_device_id snd_opti9xx_pnpids[] = {
1381da177e4SLinus Torvalds #ifndef OPTi93X
1391da177e4SLinus Torvalds 	/* OPTi 82C924 */
140fd8d4735SKrzysztof Helt 	{ .id = "OPT0924",
141fd8d4735SKrzysztof Helt 	  .devs = { { "OPT0000" }, { "OPT0002" }, { "OPT0005" } },
142fd8d4735SKrzysztof Helt 	  .driver_data = 0x0924 },
1431da177e4SLinus Torvalds 	/* OPTi 82C925 */
144fd8d4735SKrzysztof Helt 	{ .id = "OPT0925",
145fd8d4735SKrzysztof Helt 	  .devs = { { "OPT9250" }, { "OPT0002" }, { "OPT0005" } },
146fd8d4735SKrzysztof Helt 	  .driver_data = 0x0925 },
1471da177e4SLinus Torvalds #else
1481da177e4SLinus Torvalds 	/* OPTi 82C931/3 */
149fd8d4735SKrzysztof Helt 	{ .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } },
150fd8d4735SKrzysztof Helt 	  .driver_data = 0x0931 },
1511da177e4SLinus Torvalds #endif	/* OPTi93X */
1521da177e4SLinus Torvalds 	{ .id = "" }
1531da177e4SLinus Torvalds };
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids);
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds #endif	/* CONFIG_PNP */
1581da177e4SLinus Torvalds 
159fb615499STakashi Iwai #define DEV_NAME KBUILD_MODNAME
1601da177e4SLinus Torvalds 
16180e0a7c0STakashi Iwai static const char * const snd_opti9xx_names[] = {
162af901ca1SAndré Goddard Rosa 	"unknown",
1631da177e4SLinus Torvalds 	"82C928",	"82C929",
1641da177e4SLinus Torvalds 	"82C924",	"82C925",
1651da177e4SLinus Torvalds 	"82C930",	"82C931",	"82C933"
1661da177e4SLinus Torvalds };
1671da177e4SLinus Torvalds 
snd_opti9xx_init(struct snd_opti9xx * chip,unsigned short hardware)1681bff292eSBill Pemberton static int snd_opti9xx_init(struct snd_opti9xx *chip,
1695e24c1c1STakashi Iwai 			    unsigned short hardware)
1701da177e4SLinus Torvalds {
17180e0a7c0STakashi Iwai 	static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds 	chip->hardware = hardware;
1741da177e4SLinus Torvalds 	strcpy(chip->name, snd_opti9xx_names[hardware]);
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 	spin_lock_init(&chip->lock);
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds 	chip->irq = -1;
1791da177e4SLinus Torvalds 
180fd8d4735SKrzysztof Helt #ifndef OPTi93X
181fd8d4735SKrzysztof Helt #ifdef CONFIG_PNP
182fd8d4735SKrzysztof Helt 	if (isapnp && chip->mc_base)
183fd8d4735SKrzysztof Helt 		/* PnP resource gives the least 10 bits */
184fd8d4735SKrzysztof Helt 		chip->mc_base |= 0xc00;
18589c0ac7cSRandy Dunlap 	else
186fd8d4735SKrzysztof Helt #endif	/* CONFIG_PNP */
18789c0ac7cSRandy Dunlap 	{
188fd8d4735SKrzysztof Helt 		chip->mc_base = 0xf8c;
189fd8d4735SKrzysztof Helt 		chip->mc_base_size = opti9xx_mc_size[hardware];
190fd8d4735SKrzysztof Helt 	}
191fd8d4735SKrzysztof Helt #else
192fd8d4735SKrzysztof Helt 		chip->mc_base_size = opti9xx_mc_size[hardware];
193fd8d4735SKrzysztof Helt #endif
194fd8d4735SKrzysztof Helt 
1951da177e4SLinus Torvalds 	switch (hardware) {
1961da177e4SLinus Torvalds #ifndef OPTi93X
1971da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
1981da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
1991da177e4SLinus Torvalds 		chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3;
2001da177e4SLinus Torvalds 		chip->pwd_reg = 3;
2011da177e4SLinus Torvalds 		break;
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
2041da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
2051da177e4SLinus Torvalds 		chip->password = 0xe5;
2061da177e4SLinus Torvalds 		chip->pwd_reg = 3;
2071da177e4SLinus Torvalds 		break;
2081da177e4SLinus Torvalds #else	/* OPTi93X */
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930:
2111da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
2121da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
2131da177e4SLinus Torvalds 		chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d;
21410a3061aSOndrej Zary 		if (!chip->mc_indir_index)
2151da177e4SLinus Torvalds 			chip->mc_indir_index = 0xe0e;
2161da177e4SLinus Torvalds 		chip->password = 0xe4;
2171da177e4SLinus Torvalds 		chip->pwd_reg = 0;
2181da177e4SLinus Torvalds 		break;
2191da177e4SLinus Torvalds #endif	/* OPTi93X */
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds 	default:
222*40b15de3STakashi Iwai 		dev_err(chip->card->dev, "chip %d not supported\n", hardware);
2231da177e4SLinus Torvalds 		return -ENODEV;
2241da177e4SLinus Torvalds 	}
2251da177e4SLinus Torvalds 	return 0;
2261da177e4SLinus Torvalds }
2271da177e4SLinus Torvalds 
snd_opti9xx_read(struct snd_opti9xx * chip,unsigned char reg)228346c7a68STakashi Iwai static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,
2291da177e4SLinus Torvalds 				      unsigned char reg)
2301da177e4SLinus Torvalds {
2311da177e4SLinus Torvalds 	unsigned long flags;
2321da177e4SLinus Torvalds 	unsigned char retval = 0xff;
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
2351da177e4SLinus Torvalds 	outb(chip->password, chip->mc_base + chip->pwd_reg);
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	switch (chip->hardware) {
2381da177e4SLinus Torvalds #ifndef OPTi93X
2391da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
2401da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
2411da177e4SLinus Torvalds 		if (reg > 7) {
2421da177e4SLinus Torvalds 			outb(reg, chip->mc_base + 8);
2431da177e4SLinus Torvalds 			outb(chip->password, chip->mc_base + chip->pwd_reg);
2441da177e4SLinus Torvalds 			retval = inb(chip->mc_base + 9);
2451da177e4SLinus Torvalds 			break;
2461da177e4SLinus Torvalds 		}
247c0dbbdadSGustavo A. R. Silva 		fallthrough;
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
2501da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
2511da177e4SLinus Torvalds 		retval = inb(chip->mc_base + reg);
2521da177e4SLinus Torvalds 		break;
2531da177e4SLinus Torvalds #else	/* OPTi93X */
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930:
2561da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
2571da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
2581da177e4SLinus Torvalds 		outb(reg, chip->mc_indir_index);
2591da177e4SLinus Torvalds 		outb(chip->password, chip->mc_base + chip->pwd_reg);
2601da177e4SLinus Torvalds 		retval = inb(chip->mc_indir_index + 1);
2611da177e4SLinus Torvalds 		break;
2621da177e4SLinus Torvalds #endif	/* OPTi93X */
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds 	default:
265*40b15de3STakashi Iwai 		dev_err(chip->card->dev, "chip %d not supported\n", chip->hardware);
2661da177e4SLinus Torvalds 	}
2671da177e4SLinus Torvalds 
2681da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
2691da177e4SLinus Torvalds 	return retval;
2701da177e4SLinus Torvalds }
2711da177e4SLinus Torvalds 
snd_opti9xx_write(struct snd_opti9xx * chip,unsigned char reg,unsigned char value)272346c7a68STakashi Iwai static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
2731da177e4SLinus Torvalds 			      unsigned char value)
2741da177e4SLinus Torvalds {
2751da177e4SLinus Torvalds 	unsigned long flags;
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
2781da177e4SLinus Torvalds 	outb(chip->password, chip->mc_base + chip->pwd_reg);
2791da177e4SLinus Torvalds 
2801da177e4SLinus Torvalds 	switch (chip->hardware) {
2811da177e4SLinus Torvalds #ifndef OPTi93X
2821da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
2831da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
2841da177e4SLinus Torvalds 		if (reg > 7) {
2851da177e4SLinus Torvalds 			outb(reg, chip->mc_base + 8);
2861da177e4SLinus Torvalds 			outb(chip->password, chip->mc_base + chip->pwd_reg);
2871da177e4SLinus Torvalds 			outb(value, chip->mc_base + 9);
2881da177e4SLinus Torvalds 			break;
2891da177e4SLinus Torvalds 		}
290c0dbbdadSGustavo A. R. Silva 		fallthrough;
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
2931da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
2941da177e4SLinus Torvalds 		outb(value, chip->mc_base + reg);
2951da177e4SLinus Torvalds 		break;
2961da177e4SLinus Torvalds #else	/* OPTi93X */
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930:
2991da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
3001da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
3011da177e4SLinus Torvalds 		outb(reg, chip->mc_indir_index);
3021da177e4SLinus Torvalds 		outb(chip->password, chip->mc_base + chip->pwd_reg);
3031da177e4SLinus Torvalds 		outb(value, chip->mc_indir_index + 1);
3041da177e4SLinus Torvalds 		break;
3051da177e4SLinus Torvalds #endif	/* OPTi93X */
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 	default:
308*40b15de3STakashi Iwai 		dev_err(chip->card->dev, "chip %d not supported\n", chip->hardware);
3091da177e4SLinus Torvalds 	}
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
3121da177e4SLinus Torvalds }
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds 
snd_opti9xx_write_mask(struct snd_opti9xx * chip,unsigned char reg,unsigned char value,unsigned char mask)3155ce00760SArnd Bergmann static inline void snd_opti9xx_write_mask(struct snd_opti9xx *chip,
3165ce00760SArnd Bergmann 		unsigned char reg, unsigned char value, unsigned char mask)
3175ce00760SArnd Bergmann {
3185ce00760SArnd Bergmann 	unsigned char oldval = snd_opti9xx_read(chip, reg);
3191da177e4SLinus Torvalds 
3205ce00760SArnd Bergmann 	snd_opti9xx_write(chip, reg, (oldval & ~mask) | (value & mask));
3215ce00760SArnd Bergmann }
3221da177e4SLinus Torvalds 
snd_opti9xx_configure(struct snd_opti9xx * chip,long port,int irq,int dma1,int dma2,long mpu_port,int mpu_irq)32359b1f084STakashi Iwai static int snd_opti9xx_configure(struct snd_opti9xx *chip,
324fd8d4735SKrzysztof Helt 					   long port,
325d8ea2393SKrzysztof Helt 					   int irq, int dma1, int dma2,
326d8ea2393SKrzysztof Helt 					   long mpu_port, int mpu_irq)
3271da177e4SLinus Torvalds {
3281da177e4SLinus Torvalds 	unsigned char wss_base_bits;
3291da177e4SLinus Torvalds 	unsigned char irq_bits;
3301da177e4SLinus Torvalds 	unsigned char dma_bits;
3311da177e4SLinus Torvalds 	unsigned char mpu_port_bits = 0;
3321da177e4SLinus Torvalds 	unsigned char mpu_irq_bits;
3331da177e4SLinus Torvalds 
3341da177e4SLinus Torvalds 	switch (chip->hardware) {
3351da177e4SLinus Torvalds #ifndef OPTi93X
3361da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
337fd8d4735SKrzysztof Helt 		/* opti 929 mode (?), OPL3 clock output, audio enable */
3381da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc);
339fd8d4735SKrzysztof Helt 		/* enable wave audio */
3401da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
341c0dbbdadSGustavo A. R. Silva 		fallthrough;
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
344fd8d4735SKrzysztof Helt 		/* enable WSS mode */
3451da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
346fd8d4735SKrzysztof Helt 		/* OPL3 FM synthesis */
3471da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
348fd8d4735SKrzysztof Helt 		/* disable Sound Blaster IRQ and DMA */
3491da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
3501da177e4SLinus Torvalds #ifdef CS4231
351fd8d4735SKrzysztof Helt 		/* cs4231/4248 fix enabled */
3521da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
3531da177e4SLinus Torvalds #else
354fd8d4735SKrzysztof Helt 		/* cs4231/4248 fix disabled */
3551da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
3561da177e4SLinus Torvalds #endif	/* CS4231 */
3571da177e4SLinus Torvalds 		break;
3581da177e4SLinus Torvalds 
3591da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
3601da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
3611da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
3621da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
3631da177e4SLinus Torvalds 		/*
3641da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae);
3651da177e4SLinus Torvalds 		*/
3661da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
3671da177e4SLinus Torvalds #ifdef CS4231
3681da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
3691da177e4SLinus Torvalds #else
3701da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
3711da177e4SLinus Torvalds #endif	/* CS4231 */
3721da177e4SLinus Torvalds 		break;
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds #else	/* OPTi93X */
3751da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
37610a3061aSOndrej Zary 		/* disable 3D sound (set GPIO1 as output, low) */
37710a3061aSOndrej Zary 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(20), 0x04, 0x0c);
378c0dbbdadSGustavo A. R. Silva 		fallthrough;
379c0dbbdadSGustavo A. R. Silva 
38074ce5a46STakashi Iwai 	case OPTi9XX_HW_82C933:
381f81b953dSKrzysztof Helt 		/*
382f81b953dSKrzysztof Helt 		 * The BTC 1817DW has QS1000 wavetable which is connected
383f81b953dSKrzysztof Helt 		 * to the serial digital input of the OPTI931.
384f81b953dSKrzysztof Helt 		 */
385f81b953dSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(21), 0x82, 0xff);
386f81b953dSKrzysztof Helt 		/*
387f81b953dSKrzysztof Helt 		 * This bit sets OPTI931 to automaticaly select FM
388f81b953dSKrzysztof Helt 		 * or digital input signal.
389f81b953dSKrzysztof Helt 		 */
390f81b953dSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01);
391c0dbbdadSGustavo A. R. Silva 		fallthrough;
392c0dbbdadSGustavo A. R. Silva 
39374ce5a46STakashi Iwai 	case OPTi9XX_HW_82C930:
3943ae5f36aSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
3953ae5f36aSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
3963ae5f36aSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
3973ae5f36aSKrzysztof Helt 			(chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
3983ae5f36aSKrzysztof Helt 			0x34);
3993ae5f36aSKrzysztof Helt 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
4001da177e4SLinus Torvalds 		break;
4011da177e4SLinus Torvalds #endif	/* OPTi93X */
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds 	default:
404*40b15de3STakashi Iwai 		dev_err(chip->card->dev, "chip %d not supported\n", chip->hardware);
4051da177e4SLinus Torvalds 		return -EINVAL;
4061da177e4SLinus Torvalds 	}
4071da177e4SLinus Torvalds 
408fd8d4735SKrzysztof Helt 	/* PnP resource says it decodes only 10 bits of address */
409fd8d4735SKrzysztof Helt 	switch (port & 0x3ff) {
410fd8d4735SKrzysztof Helt 	case 0x130:
411fd8d4735SKrzysztof Helt 		chip->wss_base = 0x530;
4121da177e4SLinus Torvalds 		wss_base_bits = 0x00;
4131da177e4SLinus Torvalds 		break;
414fd8d4735SKrzysztof Helt 	case 0x204:
415fd8d4735SKrzysztof Helt 		chip->wss_base = 0x604;
4161da177e4SLinus Torvalds 		wss_base_bits = 0x03;
4171da177e4SLinus Torvalds 		break;
418fd8d4735SKrzysztof Helt 	case 0x280:
419fd8d4735SKrzysztof Helt 		chip->wss_base = 0xe80;
4201da177e4SLinus Torvalds 		wss_base_bits = 0x01;
4211da177e4SLinus Torvalds 		break;
422fd8d4735SKrzysztof Helt 	case 0x340:
423fd8d4735SKrzysztof Helt 		chip->wss_base = 0xf40;
4241da177e4SLinus Torvalds 		wss_base_bits = 0x02;
4251da177e4SLinus Torvalds 		break;
4261da177e4SLinus Torvalds 	default:
427*40b15de3STakashi Iwai 		dev_warn(chip->card->dev, "WSS port 0x%lx not valid\n", port);
4281da177e4SLinus Torvalds 		goto __skip_base;
4291da177e4SLinus Torvalds 	}
4301da177e4SLinus Torvalds 	snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds __skip_base:
433d8ea2393SKrzysztof Helt 	switch (irq) {
4341da177e4SLinus Torvalds //#ifdef OPTi93X
4351da177e4SLinus Torvalds 	case 5:
4361da177e4SLinus Torvalds 		irq_bits = 0x05;
4371da177e4SLinus Torvalds 		break;
4381da177e4SLinus Torvalds //#endif	/* OPTi93X */
4391da177e4SLinus Torvalds 	case 7:
4401da177e4SLinus Torvalds 		irq_bits = 0x01;
4411da177e4SLinus Torvalds 		break;
4421da177e4SLinus Torvalds 	case 9:
4431da177e4SLinus Torvalds 		irq_bits = 0x02;
4441da177e4SLinus Torvalds 		break;
4451da177e4SLinus Torvalds 	case 10:
4461da177e4SLinus Torvalds 		irq_bits = 0x03;
4471da177e4SLinus Torvalds 		break;
4481da177e4SLinus Torvalds 	case 11:
4491da177e4SLinus Torvalds 		irq_bits = 0x04;
4501da177e4SLinus Torvalds 		break;
4511da177e4SLinus Torvalds 	default:
452*40b15de3STakashi Iwai 		dev_warn(chip->card->dev, "WSS irq # %d not valid\n", irq);
4531da177e4SLinus Torvalds 		goto __skip_resources;
4541da177e4SLinus Torvalds 	}
4551da177e4SLinus Torvalds 
456d8ea2393SKrzysztof Helt 	switch (dma1) {
4571da177e4SLinus Torvalds 	case 0:
4581da177e4SLinus Torvalds 		dma_bits = 0x01;
4591da177e4SLinus Torvalds 		break;
4601da177e4SLinus Torvalds 	case 1:
4611da177e4SLinus Torvalds 		dma_bits = 0x02;
4621da177e4SLinus Torvalds 		break;
4631da177e4SLinus Torvalds 	case 3:
4641da177e4SLinus Torvalds 		dma_bits = 0x03;
4651da177e4SLinus Torvalds 		break;
4661da177e4SLinus Torvalds 	default:
467*40b15de3STakashi Iwai 		dev_warn(chip->card->dev, "WSS dma1 # %d not valid\n", dma1);
4681da177e4SLinus Torvalds 		goto __skip_resources;
4691da177e4SLinus Torvalds 	}
4701da177e4SLinus Torvalds 
4711da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
472d8ea2393SKrzysztof Helt 	if (dma1 == dma2) {
473*40b15de3STakashi Iwai 		dev_err(chip->card->dev, "don't want to share dmas\n");
4741da177e4SLinus Torvalds 		return -EBUSY;
4751da177e4SLinus Torvalds 	}
4761da177e4SLinus Torvalds 
477d8ea2393SKrzysztof Helt 	switch (dma2) {
4781da177e4SLinus Torvalds 	case 0:
4791da177e4SLinus Torvalds 	case 1:
4801da177e4SLinus Torvalds 		break;
4811da177e4SLinus Torvalds 	default:
482*40b15de3STakashi Iwai 		dev_warn(chip->card->dev, "WSS dma2 # %d not valid\n", dma2);
4831da177e4SLinus Torvalds 		goto __skip_resources;
4841da177e4SLinus Torvalds 	}
4851da177e4SLinus Torvalds 	dma_bits |= 0x04;
4861da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
4871da177e4SLinus Torvalds 
4881da177e4SLinus Torvalds #ifndef OPTi93X
489fd8d4735SKrzysztof Helt 	 outb(irq_bits << 3 | dma_bits, chip->wss_base);
4901da177e4SLinus Torvalds #else /* OPTi93X */
4911da177e4SLinus Torvalds 	snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));
4921da177e4SLinus Torvalds #endif /* OPTi93X */
4931da177e4SLinus Torvalds 
4941da177e4SLinus Torvalds __skip_resources:
4951da177e4SLinus Torvalds 	if (chip->hardware > OPTi9XX_HW_82C928) {
496d8ea2393SKrzysztof Helt 		switch (mpu_port) {
4971da177e4SLinus Torvalds 		case 0:
4981da177e4SLinus Torvalds 		case -1:
4991da177e4SLinus Torvalds 			break;
5001da177e4SLinus Torvalds 		case 0x300:
5011da177e4SLinus Torvalds 			mpu_port_bits = 0x03;
5021da177e4SLinus Torvalds 			break;
5031da177e4SLinus Torvalds 		case 0x310:
5041da177e4SLinus Torvalds 			mpu_port_bits = 0x02;
5051da177e4SLinus Torvalds 			break;
5061da177e4SLinus Torvalds 		case 0x320:
5071da177e4SLinus Torvalds 			mpu_port_bits = 0x01;
5081da177e4SLinus Torvalds 			break;
5091da177e4SLinus Torvalds 		case 0x330:
5101da177e4SLinus Torvalds 			mpu_port_bits = 0x00;
5111da177e4SLinus Torvalds 			break;
5121da177e4SLinus Torvalds 		default:
513*40b15de3STakashi Iwai 			dev_warn(chip->card->dev,
514d8ea2393SKrzysztof Helt 				 "MPU-401 port 0x%lx not valid\n", mpu_port);
5151da177e4SLinus Torvalds 			goto __skip_mpu;
5161da177e4SLinus Torvalds 		}
5171da177e4SLinus Torvalds 
518d8ea2393SKrzysztof Helt 		switch (mpu_irq) {
5191da177e4SLinus Torvalds 		case 5:
5201da177e4SLinus Torvalds 			mpu_irq_bits = 0x02;
5211da177e4SLinus Torvalds 			break;
5221da177e4SLinus Torvalds 		case 7:
5231da177e4SLinus Torvalds 			mpu_irq_bits = 0x03;
5241da177e4SLinus Torvalds 			break;
5251da177e4SLinus Torvalds 		case 9:
5261da177e4SLinus Torvalds 			mpu_irq_bits = 0x00;
5271da177e4SLinus Torvalds 			break;
5281da177e4SLinus Torvalds 		case 10:
5291da177e4SLinus Torvalds 			mpu_irq_bits = 0x01;
5301da177e4SLinus Torvalds 			break;
5311da177e4SLinus Torvalds 		default:
532*40b15de3STakashi Iwai 			dev_warn(chip->card->dev, "MPU-401 irq # %d not valid\n",
533d8ea2393SKrzysztof Helt 				 mpu_irq);
5341da177e4SLinus Torvalds 			goto __skip_mpu;
5351da177e4SLinus Torvalds 		}
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6),
538d8ea2393SKrzysztof Helt 			(mpu_port <= 0) ? 0x00 :
5391da177e4SLinus Torvalds 				0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
5401da177e4SLinus Torvalds 			0xf8);
5411da177e4SLinus Torvalds 	}
5421da177e4SLinus Torvalds __skip_mpu:
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 	return 0;
5451da177e4SLinus Torvalds }
5461da177e4SLinus Torvalds 
5471da177e4SLinus Torvalds #ifdef OPTi93X
5481da177e4SLinus Torvalds 
549e9d0a803SKrzysztof Helt static const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_step, -9300, 300, 0);
550e9d0a803SKrzysztof Helt static const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
551e9d0a803SKrzysztof Helt static const DECLARE_TLV_DB_SCALE(db_scale_4bit_12db_max, -3300, 300, 0);
552b2e8d7daSKrzysztof Helt 
553fdd1f6fdSTakashi Iwai static const struct snd_kcontrol_new snd_opti93x_controls[] = {
554b2e8d7daSKrzysztof Helt WSS_DOUBLE("Master Playback Switch", 0,
555b2e8d7daSKrzysztof Helt 		OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
556b2e8d7daSKrzysztof Helt WSS_DOUBLE_TLV("Master Playback Volume", 0,
557b2e8d7daSKrzysztof Helt 		OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1,
558e9d0a803SKrzysztof Helt 		db_scale_5bit_3db_step),
559e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("PCM Playback Volume", 0,
560e9d0a803SKrzysztof Helt 		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1,
561e9d0a803SKrzysztof Helt 		db_scale_5bit),
562e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("FM Playback Volume", 0,
563e9d0a803SKrzysztof Helt 		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1,
564e9d0a803SKrzysztof Helt 		db_scale_4bit_12db_max),
565b2e8d7daSKrzysztof Helt WSS_DOUBLE("Line Playback Switch", 0,
566b2e8d7daSKrzysztof Helt 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
567e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("Line Playback Volume", 0,
568e9d0a803SKrzysztof Helt 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1,
569e9d0a803SKrzysztof Helt 		db_scale_4bit_12db_max),
570b2e8d7daSKrzysztof Helt WSS_DOUBLE("Mic Playback Switch", 0,
571b2e8d7daSKrzysztof Helt 		OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
572e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("Mic Playback Volume", 0,
573e9d0a803SKrzysztof Helt 		OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1,
574e9d0a803SKrzysztof Helt 		db_scale_4bit_12db_max),
575e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("CD Playback Volume", 0,
576e9d0a803SKrzysztof Helt 		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1,
577e9d0a803SKrzysztof Helt 		db_scale_4bit_12db_max),
578b2e8d7daSKrzysztof Helt WSS_DOUBLE("Aux Playback Switch", 0,
579b2e8d7daSKrzysztof Helt 		OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
580e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("Aux Playback Volume", 0,
581e9d0a803SKrzysztof Helt 		OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1,
582e9d0a803SKrzysztof Helt 		db_scale_4bit_12db_max),
583b2e8d7daSKrzysztof Helt };
584b2e8d7daSKrzysztof Helt 
snd_opti93x_mixer(struct snd_wss * chip)5851bff292eSBill Pemberton static int snd_opti93x_mixer(struct snd_wss *chip)
586b2e8d7daSKrzysztof Helt {
587b2e8d7daSKrzysztof Helt 	struct snd_card *card;
588b2e8d7daSKrzysztof Helt 	unsigned int idx;
589b2e8d7daSKrzysztof Helt 	struct snd_ctl_elem_id id1, id2;
590b2e8d7daSKrzysztof Helt 	int err;
591b2e8d7daSKrzysztof Helt 
592b2e8d7daSKrzysztof Helt 	if (snd_BUG_ON(!chip || !chip->pcm))
593b2e8d7daSKrzysztof Helt 		return -EINVAL;
594b2e8d7daSKrzysztof Helt 
595b2e8d7daSKrzysztof Helt 	card = chip->card;
596b2e8d7daSKrzysztof Helt 
597b2e8d7daSKrzysztof Helt 	strcpy(card->mixername, chip->pcm->name);
598b2e8d7daSKrzysztof Helt 
599b2e8d7daSKrzysztof Helt 	memset(&id1, 0, sizeof(id1));
600b2e8d7daSKrzysztof Helt 	memset(&id2, 0, sizeof(id2));
601b2e8d7daSKrzysztof Helt 	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
602b2e8d7daSKrzysztof Helt 	/* reassign AUX0 switch to CD */
603b2e8d7daSKrzysztof Helt 	strcpy(id1.name, "Aux Playback Switch");
604b2e8d7daSKrzysztof Helt 	strcpy(id2.name, "CD Playback Switch");
605b2e8d7daSKrzysztof Helt 	err = snd_ctl_rename_id(card, &id1, &id2);
606b2e8d7daSKrzysztof Helt 	if (err < 0) {
607*40b15de3STakashi Iwai 		dev_err(card->dev, "Cannot rename opti93x control\n");
608b2e8d7daSKrzysztof Helt 		return err;
609b2e8d7daSKrzysztof Helt 	}
610b2e8d7daSKrzysztof Helt 	/* reassign AUX1 switch to FM */
611b2e8d7daSKrzysztof Helt 	strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
612b2e8d7daSKrzysztof Helt 	strcpy(id2.name, "FM Playback Switch");
613b2e8d7daSKrzysztof Helt 	err = snd_ctl_rename_id(card, &id1, &id2);
614b2e8d7daSKrzysztof Helt 	if (err < 0) {
615*40b15de3STakashi Iwai 		dev_err(card->dev, "Cannot rename opti93x control\n");
616b2e8d7daSKrzysztof Helt 		return err;
617b2e8d7daSKrzysztof Helt 	}
618b2e8d7daSKrzysztof Helt 	/* remove AUX1 volume */
619b2e8d7daSKrzysztof Helt 	strcpy(id1.name, "Aux Playback Volume"); id1.index = 1;
620b2e8d7daSKrzysztof Helt 	snd_ctl_remove_id(card, &id1);
621b2e8d7daSKrzysztof Helt 
622b2e8d7daSKrzysztof Helt 	/* Replace WSS volume controls with OPTi93x volume controls */
623b2e8d7daSKrzysztof Helt 	id1.index = 0;
624b2e8d7daSKrzysztof Helt 	for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
625b2e8d7daSKrzysztof Helt 		strcpy(id1.name, snd_opti93x_controls[idx].name);
626b2e8d7daSKrzysztof Helt 		snd_ctl_remove_id(card, &id1);
627b2e8d7daSKrzysztof Helt 
628b2e8d7daSKrzysztof Helt 		err = snd_ctl_add(card,
629b2e8d7daSKrzysztof Helt 				snd_ctl_new1(&snd_opti93x_controls[idx], chip));
630b2e8d7daSKrzysztof Helt 		if (err < 0)
631b2e8d7daSKrzysztof Helt 			return err;
632b2e8d7daSKrzysztof Helt 	}
633b2e8d7daSKrzysztof Helt 	return 0;
634b2e8d7daSKrzysztof Helt }
635b2e8d7daSKrzysztof Helt 
snd_opti93x_interrupt(int irq,void * dev_id)6367d12e780SDavid Howells static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
6371da177e4SLinus Torvalds {
6385f60e496SKrzysztof Helt 	struct snd_opti9xx *chip = dev_id;
6395f60e496SKrzysztof Helt 	struct snd_wss *codec = chip->codec;
6401da177e4SLinus Torvalds 	unsigned char status;
6411da177e4SLinus Torvalds 
6425f60e496SKrzysztof Helt 	if (!codec)
6435f60e496SKrzysztof Helt 		return IRQ_HANDLED;
6445f60e496SKrzysztof Helt 
6459f240a55SKrzysztof Helt 	status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11));
6461da177e4SLinus Torvalds 	if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)
6471da177e4SLinus Torvalds 		snd_pcm_period_elapsed(codec->playback_substream);
6481da177e4SLinus Torvalds 	if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) {
6497779f75fSKrzysztof Helt 		snd_wss_overrange(codec);
6501da177e4SLinus Torvalds 		snd_pcm_period_elapsed(codec->capture_substream);
6511da177e4SLinus Torvalds 	}
6521da177e4SLinus Torvalds 	outb(0x00, OPTi93X_PORT(codec, STATUS));
6531da177e4SLinus Torvalds 	return IRQ_HANDLED;
6541da177e4SLinus Torvalds }
6551da177e4SLinus Torvalds 
6561da177e4SLinus Torvalds #endif /* OPTi93X */
6571da177e4SLinus Torvalds 
snd_opti9xx_read_check(struct snd_card * card,struct snd_opti9xx * chip)6582973ee4aSTakashi Iwai static int snd_opti9xx_read_check(struct snd_card *card,
6592973ee4aSTakashi Iwai 				  struct snd_opti9xx *chip)
660e6960e19SKrzysztof Helt {
661e6960e19SKrzysztof Helt 	unsigned char value;
662e6960e19SKrzysztof Helt #ifdef OPTi93X
663e6960e19SKrzysztof Helt 	unsigned long flags;
664e6960e19SKrzysztof Helt #endif
665e6960e19SKrzysztof Helt 
6662973ee4aSTakashi Iwai 	chip->res_mc_base =
6672973ee4aSTakashi Iwai 		devm_request_region(card->dev, chip->mc_base,
6682973ee4aSTakashi Iwai 				    chip->mc_base_size, "OPTi9xx MC");
6692973ee4aSTakashi Iwai 	if (!chip->res_mc_base)
670e6960e19SKrzysztof Helt 		return -EBUSY;
671e6960e19SKrzysztof Helt #ifndef OPTi93X
672e6960e19SKrzysztof Helt 	value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
673e6960e19SKrzysztof Helt 	if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1)))
674e6960e19SKrzysztof Helt 		if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
675e6960e19SKrzysztof Helt 			return 0;
676e6960e19SKrzysztof Helt #else	/* OPTi93X */
6772973ee4aSTakashi Iwai 	chip->res_mc_indir =
6782973ee4aSTakashi Iwai 		devm_request_region(card->dev, chip->mc_indir_index, 2,
679e6960e19SKrzysztof Helt 				    "OPTi93x MC");
6802973ee4aSTakashi Iwai 	if (!chip->res_mc_indir)
681e6960e19SKrzysztof Helt 		return -EBUSY;
682e6960e19SKrzysztof Helt 
683e6960e19SKrzysztof Helt 	spin_lock_irqsave(&chip->lock, flags);
684e6960e19SKrzysztof Helt 	outb(chip->password, chip->mc_base + chip->pwd_reg);
685e6960e19SKrzysztof Helt 	outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base);
686e6960e19SKrzysztof Helt 	spin_unlock_irqrestore(&chip->lock, flags);
687e6960e19SKrzysztof Helt 
688e6960e19SKrzysztof Helt 	value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));
689e6960e19SKrzysztof Helt 	snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);
690e6960e19SKrzysztof Helt 	if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)
691e6960e19SKrzysztof Helt 		return 0;
692e6960e19SKrzysztof Helt 
6932973ee4aSTakashi Iwai 	devm_release_resource(card->dev, chip->res_mc_indir);
694e6960e19SKrzysztof Helt 	chip->res_mc_indir = NULL;
695e6960e19SKrzysztof Helt #endif	/* OPTi93X */
6962973ee4aSTakashi Iwai 	devm_release_resource(card->dev, chip->res_mc_base);
697e6960e19SKrzysztof Helt 	chip->res_mc_base = NULL;
698e6960e19SKrzysztof Helt 
699e6960e19SKrzysztof Helt 	return -ENODEV;
700e6960e19SKrzysztof Helt }
701e6960e19SKrzysztof Helt 
snd_card_opti9xx_detect(struct snd_card * card,struct snd_opti9xx * chip)7021bff292eSBill Pemberton static int snd_card_opti9xx_detect(struct snd_card *card,
7035e24c1c1STakashi Iwai 				   struct snd_opti9xx *chip)
7041da177e4SLinus Torvalds {
7051da177e4SLinus Torvalds 	int i, err;
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds #ifndef OPTi93X
7081da177e4SLinus Torvalds 	for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {
709e6960e19SKrzysztof Helt #else
7101da177e4SLinus Torvalds 	for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {
711e6960e19SKrzysztof Helt #endif
712e6960e19SKrzysztof Helt 		err = snd_opti9xx_init(chip, i);
713e6960e19SKrzysztof Helt 		if (err < 0)
7141da177e4SLinus Torvalds 			return err;
7151da177e4SLinus Torvalds 
7162973ee4aSTakashi Iwai 		err = snd_opti9xx_read_check(card, chip);
717e6960e19SKrzysztof Helt 		if (err == 0)
7181da177e4SLinus Torvalds 			return 1;
719e6960e19SKrzysztof Helt #ifdef OPTi93X
720e6960e19SKrzysztof Helt 		chip->mc_indir_index = 0;
721e6960e19SKrzysztof Helt #endif
7221da177e4SLinus Torvalds 	}
7231da177e4SLinus Torvalds 	return -ENODEV;
7241da177e4SLinus Torvalds }
7251da177e4SLinus Torvalds 
7261da177e4SLinus Torvalds #ifdef CONFIG_PNP
7271bff292eSBill Pemberton static int snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
7285e24c1c1STakashi Iwai 				struct pnp_card_link *card,
7291da177e4SLinus Torvalds 				const struct pnp_card_device_id *pid)
7301da177e4SLinus Torvalds {
7311da177e4SLinus Torvalds 	struct pnp_dev *pdev;
7321da177e4SLinus Torvalds 	int err;
733fd8d4735SKrzysztof Helt 	struct pnp_dev *devmpu;
734fd8d4735SKrzysztof Helt #ifndef OPTi93X
735fd8d4735SKrzysztof Helt 	struct pnp_dev *devmc;
736fd8d4735SKrzysztof Helt #endif
7371da177e4SLinus Torvalds 
738fd8d4735SKrzysztof Helt 	pdev = pnp_request_card_device(card, pid->devs[0].id, NULL);
739fd8d4735SKrzysztof Helt 	if (pdev == NULL)
7401da177e4SLinus Torvalds 		return -EBUSY;
741109c53f8SRene Herman 
7421da177e4SLinus Torvalds 	err = pnp_activate_dev(pdev);
7431da177e4SLinus Torvalds 	if (err < 0) {
744*40b15de3STakashi Iwai 		dev_err(chip->card->dev, "AUDIO pnp configure failure: %d\n", err);
7451da177e4SLinus Torvalds 		return err;
7461da177e4SLinus Torvalds 	}
7471da177e4SLinus Torvalds 
7481da177e4SLinus Torvalds #ifdef OPTi93X
7491da177e4SLinus Torvalds 	port = pnp_port_start(pdev, 0) - 4;
7501ea73412STakashi Iwai 	fm_port = pnp_port_start(pdev, 1) + 8;
75110a3061aSOndrej Zary 	/* adjust mc_indir_index - some cards report it at 0xe?d,
75210a3061aSOndrej Zary 	   other at 0xe?c but it really is always at 0xe?e */
75310a3061aSOndrej Zary 	chip->mc_indir_index = (pnp_port_start(pdev, 3) & ~0xf) | 0xe;
7541da177e4SLinus Torvalds #else
755fd8d4735SKrzysztof Helt 	devmc = pnp_request_card_device(card, pid->devs[2].id, NULL);
756fd8d4735SKrzysztof Helt 	if (devmc == NULL)
757fd8d4735SKrzysztof Helt 		return -EBUSY;
758fd8d4735SKrzysztof Helt 
759fd8d4735SKrzysztof Helt 	err = pnp_activate_dev(devmc);
760fd8d4735SKrzysztof Helt 	if (err < 0) {
761*40b15de3STakashi Iwai 		dev_err(chip->card->dev, "MC pnp configure failure: %d\n", err);
762fd8d4735SKrzysztof Helt 		return err;
763fd8d4735SKrzysztof Helt 	}
764fd8d4735SKrzysztof Helt 
7651da177e4SLinus Torvalds 	port = pnp_port_start(pdev, 1);
7661ea73412STakashi Iwai 	fm_port = pnp_port_start(pdev, 2) + 8;
767fd8d4735SKrzysztof Helt 	/*
768fd8d4735SKrzysztof Helt 	 * The MC(0) is never accessed and card does not
769fd8d4735SKrzysztof Helt 	 * include it in the PnP resource range. OPTI93x include it.
770fd8d4735SKrzysztof Helt 	 */
771fd8d4735SKrzysztof Helt 	chip->mc_base = pnp_port_start(devmc, 0) - 1;
772fd8d4735SKrzysztof Helt 	chip->mc_base_size = pnp_port_len(devmc, 0) + 1;
7731da177e4SLinus Torvalds #endif	/* OPTi93X */
7741da177e4SLinus Torvalds 	irq = pnp_irq(pdev, 0);
7751da177e4SLinus Torvalds 	dma1 = pnp_dma(pdev, 0);
7761da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
7771da177e4SLinus Torvalds 	dma2 = pnp_dma(pdev, 1);
7781da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
7791da177e4SLinus Torvalds 
780fd8d4735SKrzysztof Helt 	devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
781fd8d4735SKrzysztof Helt 
782fd8d4735SKrzysztof Helt 	if (devmpu && mpu_port > 0) {
783fd8d4735SKrzysztof Helt 		err = pnp_activate_dev(devmpu);
7841da177e4SLinus Torvalds 		if (err < 0) {
785*40b15de3STakashi Iwai 			dev_err(chip->card->dev, "MPU401 pnp configure failure\n");
7861da177e4SLinus Torvalds 			mpu_port = -1;
7871da177e4SLinus Torvalds 		} else {
788fd8d4735SKrzysztof Helt 			mpu_port = pnp_port_start(devmpu, 0);
789fd8d4735SKrzysztof Helt 			mpu_irq = pnp_irq(devmpu, 0);
7901da177e4SLinus Torvalds 		}
7911da177e4SLinus Torvalds 	}
7921da177e4SLinus Torvalds 	return pid->driver_data;
7931da177e4SLinus Torvalds }
7941da177e4SLinus Torvalds #endif	/* CONFIG_PNP */
7951da177e4SLinus Torvalds 
7961bff292eSBill Pemberton static int snd_opti9xx_probe(struct snd_card *card)
7971da177e4SLinus Torvalds {
79880e0a7c0STakashi Iwai 	static const long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
7991da177e4SLinus Torvalds 	int error;
800d8ea2393SKrzysztof Helt 	int xdma2;
80199a0b768STakashi Iwai 	struct snd_opti9xx *chip = card->private_data;
8027779f75fSKrzysztof Helt 	struct snd_wss *codec;
803346c7a68STakashi Iwai 	struct snd_rawmidi *rmidi;
804346c7a68STakashi Iwai 	struct snd_hwdep *synth;
8051da177e4SLinus Torvalds 
8061da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
807d8ea2393SKrzysztof Helt 	xdma2 = dma2;
808a0d9274cSRene Herman #else
809d8ea2393SKrzysztof Helt 	xdma2 = -1;
8101da177e4SLinus Torvalds #endif
8111da177e4SLinus Torvalds 
812d8ea2393SKrzysztof Helt 	if (port == SNDRV_AUTO_PORT) {
813d8ea2393SKrzysztof Helt 		port = snd_legacy_find_free_ioport(possible_ports, 4);
814d8ea2393SKrzysztof Helt 		if (port < 0) {
815*40b15de3STakashi Iwai 			dev_err(card->dev, "unable to find a free WSS port\n");
8161da177e4SLinus Torvalds 			return -EBUSY;
8171da177e4SLinus Torvalds 		}
8181da177e4SLinus Torvalds 	}
819d8ea2393SKrzysztof Helt 	error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2,
820d8ea2393SKrzysztof Helt 				      mpu_port, mpu_irq);
8217779f75fSKrzysztof Helt 	if (error)
8221da177e4SLinus Torvalds 		return error;
8231da177e4SLinus Torvalds 
824fd8d4735SKrzysztof Helt 	error = snd_wss_create(card, chip->wss_base + 4, -1, irq, dma1, xdma2,
825a0d9274cSRene Herman #ifdef OPTi93X
8267779f75fSKrzysztof Helt 			       WSS_HW_OPTI93X, WSS_HWSHARE_IRQ,
827a0d9274cSRene Herman #else
828a0d9274cSRene Herman 			       WSS_HW_DETECT, 0,
8299f240a55SKrzysztof Helt #endif
8307779f75fSKrzysztof Helt 			       &codec);
8317779f75fSKrzysztof Helt 	if (error < 0)
8321da177e4SLinus Torvalds 		return error;
8339f240a55SKrzysztof Helt 	chip->codec = codec;
834fa60c065SLars-Peter Clausen 	error = snd_wss_pcm(codec, 0);
8355664daa1SKrzysztof Helt 	if (error < 0)
8365664daa1SKrzysztof Helt 		return error;
8377779f75fSKrzysztof Helt 	error = snd_wss_mixer(codec);
8387779f75fSKrzysztof Helt 	if (error < 0)
8391da177e4SLinus Torvalds 		return error;
840b2e8d7daSKrzysztof Helt #ifdef OPTi93X
841b2e8d7daSKrzysztof Helt 	error = snd_opti93x_mixer(codec);
842b2e8d7daSKrzysztof Helt 	if (error < 0)
843b2e8d7daSKrzysztof Helt 		return error;
844b2e8d7daSKrzysztof Helt #endif
8459f240a55SKrzysztof Helt #ifdef CS4231
846fa60c065SLars-Peter Clausen 	error = snd_wss_timer(codec, 0);
8477779f75fSKrzysztof Helt 	if (error < 0)
8481da177e4SLinus Torvalds 		return error;
8495664daa1SKrzysztof Helt #endif
8505664daa1SKrzysztof Helt #ifdef OPTi93X
8512973ee4aSTakashi Iwai 	error = devm_request_irq(card->dev, irq, snd_opti93x_interrupt,
85288e24c3aSYong Zhang 				 0, DEV_NAME" - WSS", chip);
8539f240a55SKrzysztof Helt 	if (error < 0) {
854*40b15de3STakashi Iwai 		dev_err(card->dev, "opti9xx: can't grab IRQ %d\n", irq);
8559f240a55SKrzysztof Helt 		return error;
8569f240a55SKrzysztof Helt 	}
8579f240a55SKrzysztof Helt #endif
858d8ea2393SKrzysztof Helt 	chip->irq = irq;
85916d9fb1dSTakashi Iwai 	card->sync_irq = chip->irq;
8601da177e4SLinus Torvalds 	strcpy(card->driver, chip->name);
8611da177e4SLinus Torvalds 	sprintf(card->shortname, "OPTi %s", card->driver);
8621da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
863bc44e10aSTakashi Iwai 	scnprintf(card->longname, sizeof(card->longname),
86424d22077SArnd Bergmann 		  "%s, %s at 0x%lx, irq %d, dma %d&%d",
865fa60c065SLars-Peter Clausen 		  card->shortname, codec->pcm->name,
866fd8d4735SKrzysztof Helt 		  chip->wss_base + 4, irq, dma1, xdma2);
8671da177e4SLinus Torvalds #else
868bc44e10aSTakashi Iwai 	scnprintf(card->longname, sizeof(card->longname),
86924d22077SArnd Bergmann 		  "%s, %s at 0x%lx, irq %d, dma %d",
870fa60c065SLars-Peter Clausen 		  card->shortname, codec->pcm->name, chip->wss_base + 4, irq,
871fa60c065SLars-Peter Clausen 		  dma1);
8721da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
8731da177e4SLinus Torvalds 
874d8ea2393SKrzysztof Helt 	if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
8751da177e4SLinus Torvalds 		rmidi = NULL;
876d8ea2393SKrzysztof Helt 	else {
877d8ea2393SKrzysztof Helt 		error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
878dba8b469SClemens Ladisch 				mpu_port, 0, mpu_irq, &rmidi);
879d8ea2393SKrzysztof Helt 		if (error)
880*40b15de3STakashi Iwai 			dev_warn(card->dev, "no MPU-401 device at 0x%lx?\n",
881d8ea2393SKrzysztof Helt 				 mpu_port);
882d8ea2393SKrzysztof Helt 	}
8831da177e4SLinus Torvalds 
884d8ea2393SKrzysztof Helt 	if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
885346c7a68STakashi Iwai 		struct snd_opl3 *opl3 = NULL;
8861da177e4SLinus Torvalds #ifndef OPTi93X
8871da177e4SLinus Torvalds 		if (chip->hardware == OPTi9XX_HW_82C928 ||
8881da177e4SLinus Torvalds 		    chip->hardware == OPTi9XX_HW_82C929 ||
8891da177e4SLinus Torvalds 		    chip->hardware == OPTi9XX_HW_82C924) {
890346c7a68STakashi Iwai 			struct snd_opl4 *opl4;
8911da177e4SLinus Torvalds 			/* assume we have an OPL4 */
8921da177e4SLinus Torvalds 			snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
8931da177e4SLinus Torvalds 					       0x20, 0x20);
894d8ea2393SKrzysztof Helt 			if (snd_opl4_create(card, fm_port, fm_port - 8,
8951da177e4SLinus Torvalds 					    2, &opl3, &opl4) < 0) {
8961da177e4SLinus Torvalds 				/* no luck, use OPL3 instead */
8971da177e4SLinus Torvalds 				snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
8981da177e4SLinus Torvalds 						       0x00, 0x20);
8991da177e4SLinus Torvalds 			}
9001da177e4SLinus Torvalds 		}
9011da177e4SLinus Torvalds #endif	/* !OPTi93X */
902d8ea2393SKrzysztof Helt 		if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2,
9031da177e4SLinus Torvalds 					     OPL3_HW_AUTO, 0, &opl3) < 0) {
904*40b15de3STakashi Iwai 			dev_warn(card->dev, "no OPL device at 0x%lx-0x%lx\n",
905d8ea2393SKrzysztof Helt 				 fm_port, fm_port + 4 - 1);
9061da177e4SLinus Torvalds 		}
9071da177e4SLinus Torvalds 		if (opl3) {
908aa9c293aSKrzysztof Helt 			error = snd_opl3_hwdep_new(opl3, 0, 1, &synth);
909aa9c293aSKrzysztof Helt 			if (error < 0)
9101da177e4SLinus Torvalds 				return error;
9111da177e4SLinus Torvalds 		}
9121da177e4SLinus Torvalds 	}
9131da177e4SLinus Torvalds 
91499a0b768STakashi Iwai 	return snd_card_register(card);
91599a0b768STakashi Iwai }
91699a0b768STakashi Iwai 
9174323cc4dSTakashi Iwai static int snd_opti9xx_card_new(struct device *pdev, struct snd_card **cardp)
91899a0b768STakashi Iwai {
91999a0b768STakashi Iwai 	struct snd_card *card;
920b1a0aac0STakashi Iwai 	int err;
92199a0b768STakashi Iwai 
9222973ee4aSTakashi Iwai 	err = snd_devm_card_new(pdev, index, id, THIS_MODULE,
923c95eadd2STakashi Iwai 				sizeof(struct snd_opti9xx), &card);
924c95eadd2STakashi Iwai 	if (err < 0)
9253e7fb9f7STakashi Iwai 		return err;
9263e7fb9f7STakashi Iwai 	*cardp = card;
9273e7fb9f7STakashi Iwai 	return 0;
92899a0b768STakashi Iwai }
92999a0b768STakashi Iwai 
9301bff292eSBill Pemberton static int snd_opti9xx_isa_match(struct device *devptr,
9315e24c1c1STakashi Iwai 				 unsigned int dev)
9325e24c1c1STakashi Iwai {
933101f6f4bSTakashi Iwai #ifdef CONFIG_PNP
9345e24c1c1STakashi Iwai 	if (snd_opti9xx_pnp_is_probed)
9355e24c1c1STakashi Iwai 		return 0;
9365e24c1c1STakashi Iwai 	if (isapnp)
9375e24c1c1STakashi Iwai 		return 0;
938101f6f4bSTakashi Iwai #endif
9395e24c1c1STakashi Iwai 	return 1;
9405e24c1c1STakashi Iwai }
9415e24c1c1STakashi Iwai 
9421bff292eSBill Pemberton static int snd_opti9xx_isa_probe(struct device *devptr,
9435e24c1c1STakashi Iwai 				 unsigned int dev)
94499a0b768STakashi Iwai {
94599a0b768STakashi Iwai 	struct snd_card *card;
94699a0b768STakashi Iwai 	int error;
94780e0a7c0STakashi Iwai 	static const long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1};
94899a0b768STakashi Iwai #ifdef OPTi93X
94980e0a7c0STakashi Iwai 	static const int possible_irqs[] = {5, 9, 10, 11, 7, -1};
95099a0b768STakashi Iwai #else
95180e0a7c0STakashi Iwai 	static const int possible_irqs[] = {9, 10, 11, 7, -1};
95299a0b768STakashi Iwai #endif	/* OPTi93X */
95380e0a7c0STakashi Iwai 	static const int possible_mpu_irqs[] = {5, 9, 10, 7, -1};
95480e0a7c0STakashi Iwai 	static const int possible_dma1s[] = {3, 1, 0, -1};
95599a0b768STakashi Iwai #if defined(CS4231) || defined(OPTi93X)
95680e0a7c0STakashi Iwai 	static const int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
95799a0b768STakashi Iwai #endif	/* CS4231 || OPTi93X */
95899a0b768STakashi Iwai 
95999a0b768STakashi Iwai 	if (mpu_port == SNDRV_AUTO_PORT) {
960913ad3a3STakashi Iwai 		mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2);
961913ad3a3STakashi Iwai 		if (mpu_port < 0) {
962*40b15de3STakashi Iwai 			dev_err(devptr, "unable to find a free MPU401 port\n");
96399a0b768STakashi Iwai 			return -EBUSY;
96499a0b768STakashi Iwai 		}
96599a0b768STakashi Iwai 	}
96699a0b768STakashi Iwai 	if (irq == SNDRV_AUTO_IRQ) {
967913ad3a3STakashi Iwai 		irq = snd_legacy_find_free_irq(possible_irqs);
968913ad3a3STakashi Iwai 		if (irq < 0) {
969*40b15de3STakashi Iwai 			dev_err(devptr, "unable to find a free IRQ\n");
97099a0b768STakashi Iwai 			return -EBUSY;
97199a0b768STakashi Iwai 		}
97299a0b768STakashi Iwai 	}
97399a0b768STakashi Iwai 	if (mpu_irq == SNDRV_AUTO_IRQ) {
974913ad3a3STakashi Iwai 		mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs);
975913ad3a3STakashi Iwai 		if (mpu_irq < 0) {
976*40b15de3STakashi Iwai 			dev_err(devptr, "unable to find a free MPU401 IRQ\n");
97799a0b768STakashi Iwai 			return -EBUSY;
97899a0b768STakashi Iwai 		}
97999a0b768STakashi Iwai 	}
98099a0b768STakashi Iwai 	if (dma1 == SNDRV_AUTO_DMA) {
981913ad3a3STakashi Iwai 		dma1 = snd_legacy_find_free_dma(possible_dma1s);
982913ad3a3STakashi Iwai 		if (dma1 < 0) {
983*40b15de3STakashi Iwai 			dev_err(devptr, "unable to find a free DMA1\n");
98499a0b768STakashi Iwai 			return -EBUSY;
98599a0b768STakashi Iwai 		}
98699a0b768STakashi Iwai 	}
98799a0b768STakashi Iwai #if defined(CS4231) || defined(OPTi93X)
98899a0b768STakashi Iwai 	if (dma2 == SNDRV_AUTO_DMA) {
989913ad3a3STakashi Iwai 		dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]);
990913ad3a3STakashi Iwai 		if (dma2 < 0) {
991*40b15de3STakashi Iwai 			dev_err(devptr, "unable to find a free DMA2\n");
99299a0b768STakashi Iwai 			return -EBUSY;
99399a0b768STakashi Iwai 		}
99499a0b768STakashi Iwai 	}
99599a0b768STakashi Iwai #endif
99699a0b768STakashi Iwai 
9974323cc4dSTakashi Iwai 	error = snd_opti9xx_card_new(devptr, &card);
9983e7fb9f7STakashi Iwai 	if (error < 0)
9993e7fb9f7STakashi Iwai 		return error;
100099a0b768STakashi Iwai 
1001913ad3a3STakashi Iwai 	error = snd_card_opti9xx_detect(card, card->private_data);
10022973ee4aSTakashi Iwai 	if (error < 0)
10031da177e4SLinus Torvalds 		return error;
1004913ad3a3STakashi Iwai 	error = snd_opti9xx_probe(card);
10052973ee4aSTakashi Iwai 	if (error < 0)
100699a0b768STakashi Iwai 		return error;
10075e24c1c1STakashi Iwai 	dev_set_drvdata(devptr, card);
10081da177e4SLinus Torvalds 	return 0;
10091da177e4SLinus Torvalds }
10101da177e4SLinus Torvalds 
10115dd25072SOndrej Zary #ifdef CONFIG_PM
10125dd25072SOndrej Zary static int snd_opti9xx_suspend(struct snd_card *card)
10135dd25072SOndrej Zary {
10145dd25072SOndrej Zary 	struct snd_opti9xx *chip = card->private_data;
10155dd25072SOndrej Zary 
10165dd25072SOndrej Zary 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
10175dd25072SOndrej Zary 	chip->codec->suspend(chip->codec);
10185dd25072SOndrej Zary 	return 0;
10195dd25072SOndrej Zary }
10205dd25072SOndrej Zary 
10215dd25072SOndrej Zary static int snd_opti9xx_resume(struct snd_card *card)
10225dd25072SOndrej Zary {
10235dd25072SOndrej Zary 	struct snd_opti9xx *chip = card->private_data;
10245dd25072SOndrej Zary 	int error, xdma2;
10255dd25072SOndrej Zary #if defined(CS4231) || defined(OPTi93X)
10265dd25072SOndrej Zary 	xdma2 = dma2;
10275dd25072SOndrej Zary #else
10285dd25072SOndrej Zary 	xdma2 = -1;
10295dd25072SOndrej Zary #endif
10305dd25072SOndrej Zary 
10315dd25072SOndrej Zary 	error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2,
10325dd25072SOndrej Zary 				      mpu_port, mpu_irq);
10335dd25072SOndrej Zary 	if (error)
10345dd25072SOndrej Zary 		return error;
10355dd25072SOndrej Zary 	chip->codec->resume(chip->codec);
10365dd25072SOndrej Zary 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
10375dd25072SOndrej Zary 	return 0;
10385dd25072SOndrej Zary }
10395dd25072SOndrej Zary 
10405dd25072SOndrej Zary static int snd_opti9xx_isa_suspend(struct device *dev, unsigned int n,
10415dd25072SOndrej Zary 				   pm_message_t state)
10425dd25072SOndrej Zary {
10435dd25072SOndrej Zary 	return snd_opti9xx_suspend(dev_get_drvdata(dev));
10445dd25072SOndrej Zary }
10455dd25072SOndrej Zary 
10465dd25072SOndrej Zary static int snd_opti9xx_isa_resume(struct device *dev, unsigned int n)
10475dd25072SOndrej Zary {
10485dd25072SOndrej Zary 	return snd_opti9xx_resume(dev_get_drvdata(dev));
10495dd25072SOndrej Zary }
10505dd25072SOndrej Zary #endif
10515dd25072SOndrej Zary 
10525e24c1c1STakashi Iwai static struct isa_driver snd_opti9xx_driver = {
10535e24c1c1STakashi Iwai 	.match		= snd_opti9xx_isa_match,
10545e24c1c1STakashi Iwai 	.probe		= snd_opti9xx_isa_probe,
10555dd25072SOndrej Zary #ifdef CONFIG_PM
10565dd25072SOndrej Zary 	.suspend	= snd_opti9xx_isa_suspend,
10575dd25072SOndrej Zary 	.resume		= snd_opti9xx_isa_resume,
10585dd25072SOndrej Zary #endif
105999a0b768STakashi Iwai 	.driver		= {
106083c51c0aSRene Herman 		.name	= DEV_NAME
106199a0b768STakashi Iwai 	},
106299a0b768STakashi Iwai };
106399a0b768STakashi Iwai 
10641da177e4SLinus Torvalds #ifdef CONFIG_PNP
10651bff292eSBill Pemberton static int snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
106699a0b768STakashi Iwai 				 const struct pnp_card_device_id *pid)
106799a0b768STakashi Iwai {
106899a0b768STakashi Iwai 	struct snd_card *card;
106999a0b768STakashi Iwai 	int error, hw;
107099a0b768STakashi Iwai 	struct snd_opti9xx *chip;
107199a0b768STakashi Iwai 
107299a0b768STakashi Iwai 	if (snd_opti9xx_pnp_is_probed)
107399a0b768STakashi Iwai 		return -EBUSY;
107499a0b768STakashi Iwai 	if (! isapnp)
107599a0b768STakashi Iwai 		return -ENODEV;
10764323cc4dSTakashi Iwai 	error = snd_opti9xx_card_new(&pcard->card->dev, &card);
10773e7fb9f7STakashi Iwai 	if (error < 0)
10783e7fb9f7STakashi Iwai 		return error;
107999a0b768STakashi Iwai 	chip = card->private_data;
1080*40b15de3STakashi Iwai 	chip->card = card;
108199a0b768STakashi Iwai 
108299a0b768STakashi Iwai 	hw = snd_card_opti9xx_pnp(chip, pcard, pid);
108399a0b768STakashi Iwai 	switch (hw) {
108499a0b768STakashi Iwai 	case 0x0924:
108599a0b768STakashi Iwai 		hw = OPTi9XX_HW_82C924;
108699a0b768STakashi Iwai 		break;
108799a0b768STakashi Iwai 	case 0x0925:
108899a0b768STakashi Iwai 		hw = OPTi9XX_HW_82C925;
108999a0b768STakashi Iwai 		break;
109099a0b768STakashi Iwai 	case 0x0931:
109199a0b768STakashi Iwai 		hw = OPTi9XX_HW_82C931;
109299a0b768STakashi Iwai 		break;
109399a0b768STakashi Iwai 	default:
109499a0b768STakashi Iwai 		return -ENODEV;
109599a0b768STakashi Iwai 	}
109699a0b768STakashi Iwai 
1097913ad3a3STakashi Iwai 	error = snd_opti9xx_init(chip, hw);
10982973ee4aSTakashi Iwai 	if (error)
109999a0b768STakashi Iwai 		return error;
11002973ee4aSTakashi Iwai 	error = snd_opti9xx_read_check(card, chip);
1101e24ef488SColin Ian King 	if (error) {
1102*40b15de3STakashi Iwai 		dev_err(card->dev, "OPTI chip not found\n");
1103e6960e19SKrzysztof Helt 		return error;
1104e24ef488SColin Ian King 	}
1105913ad3a3STakashi Iwai 	error = snd_opti9xx_probe(card);
11062973ee4aSTakashi Iwai 	if (error < 0)
110799a0b768STakashi Iwai 		return error;
110899a0b768STakashi Iwai 	pnp_set_card_drvdata(pcard, card);
110999a0b768STakashi Iwai 	snd_opti9xx_pnp_is_probed = 1;
111099a0b768STakashi Iwai 	return 0;
111199a0b768STakashi Iwai }
111299a0b768STakashi Iwai 
11131bff292eSBill Pemberton static void snd_opti9xx_pnp_remove(struct pnp_card_link *pcard)
11141da177e4SLinus Torvalds {
111599a0b768STakashi Iwai 	snd_opti9xx_pnp_is_probed = 0;
11161da177e4SLinus Torvalds }
11171da177e4SLinus Torvalds 
11185dd25072SOndrej Zary #ifdef CONFIG_PM
11195dd25072SOndrej Zary static int snd_opti9xx_pnp_suspend(struct pnp_card_link *pcard,
11205dd25072SOndrej Zary 				   pm_message_t state)
11215dd25072SOndrej Zary {
11225dd25072SOndrej Zary 	return snd_opti9xx_suspend(pnp_get_card_drvdata(pcard));
11235dd25072SOndrej Zary }
11245dd25072SOndrej Zary 
11255dd25072SOndrej Zary static int snd_opti9xx_pnp_resume(struct pnp_card_link *pcard)
11265dd25072SOndrej Zary {
11275dd25072SOndrej Zary 	return snd_opti9xx_resume(pnp_get_card_drvdata(pcard));
11285dd25072SOndrej Zary }
11295dd25072SOndrej Zary #endif
11305dd25072SOndrej Zary 
11311da177e4SLinus Torvalds static struct pnp_card_driver opti9xx_pnpc_driver = {
11321da177e4SLinus Torvalds 	.flags		= PNP_DRIVER_RES_DISABLE,
1133fb615499STakashi Iwai 	.name		= DEV_NAME,
11341da177e4SLinus Torvalds 	.id_table	= snd_opti9xx_pnpids,
113599a0b768STakashi Iwai 	.probe		= snd_opti9xx_pnp_probe,
11361bff292eSBill Pemberton 	.remove		= snd_opti9xx_pnp_remove,
11375dd25072SOndrej Zary #ifdef CONFIG_PM
11385dd25072SOndrej Zary 	.suspend	= snd_opti9xx_pnp_suspend,
11395dd25072SOndrej Zary 	.resume		= snd_opti9xx_pnp_resume,
11405dd25072SOndrej Zary #endif
11411da177e4SLinus Torvalds };
11421da177e4SLinus Torvalds #endif
11431da177e4SLinus Torvalds 
114499a0b768STakashi Iwai #ifdef OPTi93X
114599a0b768STakashi Iwai #define CHIP_NAME	"82C93x"
114699a0b768STakashi Iwai #else
114799a0b768STakashi Iwai #define CHIP_NAME	"82C92x"
114899a0b768STakashi Iwai #endif
114999a0b768STakashi Iwai 
11501da177e4SLinus Torvalds static int __init alsa_card_opti9xx_init(void)
11511da177e4SLinus Torvalds {
11520bbbc4caSTakashi Iwai #ifdef CONFIG_PNP
115399a0b768STakashi Iwai 	pnp_register_card_driver(&opti9xx_pnpc_driver);
115499a0b768STakashi Iwai 	if (snd_opti9xx_pnp_is_probed)
11551da177e4SLinus Torvalds 		return 0;
1156101f6f4bSTakashi Iwai 	pnp_unregister_card_driver(&opti9xx_pnpc_driver);
11570bbbc4caSTakashi Iwai #endif
11585e24c1c1STakashi Iwai 	return isa_register_driver(&snd_opti9xx_driver, 1);
11591da177e4SLinus Torvalds }
11601da177e4SLinus Torvalds 
11611da177e4SLinus Torvalds static void __exit alsa_card_opti9xx_exit(void)
11621da177e4SLinus Torvalds {
1163f7a9275dSClemens Ladisch 	if (!snd_opti9xx_pnp_is_probed) {
11645e24c1c1STakashi Iwai 		isa_unregister_driver(&snd_opti9xx_driver);
11655e24c1c1STakashi Iwai 		return;
1166f7a9275dSClemens Ladisch 	}
11670bbbc4caSTakashi Iwai #ifdef CONFIG_PNP
11681da177e4SLinus Torvalds 	pnp_unregister_card_driver(&opti9xx_pnpc_driver);
11690bbbc4caSTakashi Iwai #endif
11701da177e4SLinus Torvalds }
11711da177e4SLinus Torvalds 
11721da177e4SLinus Torvalds module_init(alsa_card_opti9xx_init)
11731da177e4SLinus Torvalds module_exit(alsa_card_opti9xx_exit)
1174