xref: /linux/sound/isa/opti9xx/opti92x-ad1848.c (revision 65ca68b30073473583f6ca2f463cbd94ade43ddb)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds     card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards.
31da177e4SLinus Torvalds     Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it>
41da177e4SLinus Torvalds 
51da177e4SLinus Torvalds     Part of this code was developed at the Italian Ministry of Air Defence,
61da177e4SLinus Torvalds     Sixth Division (oh, che pace ...), Rome.
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds     Thanks to Maria Grazia Pollarini, Salvatore Vassallo.
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds     This program is free software; you can redistribute it and/or modify
111da177e4SLinus Torvalds     it under the terms of the GNU General Public License as published by
121da177e4SLinus Torvalds     the Free Software Foundation; either version 2 of the License, or
131da177e4SLinus Torvalds     (at your option) any later version.
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds     This program is distributed in the hope that it will be useful,
161da177e4SLinus Torvalds     but WITHOUT ANY WARRANTY; without even the implied warranty of
171da177e4SLinus Torvalds     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
181da177e4SLinus Torvalds     GNU General Public License for more details.
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds     You should have received a copy of the GNU General Public License
211da177e4SLinus Torvalds     along with this program; if not, write to the Free Software
221da177e4SLinus Torvalds     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
231da177e4SLinus Torvalds */
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds #include <sound/driver.h>
271da177e4SLinus Torvalds #include <linux/init.h>
2899a0b768STakashi Iwai #include <linux/err.h>
2999a0b768STakashi Iwai #include <linux/platform_device.h>
3099a0b768STakashi Iwai #include <linux/delay.h>
311da177e4SLinus Torvalds #include <linux/slab.h>
321da177e4SLinus Torvalds #include <linux/pnp.h>
331da177e4SLinus Torvalds #include <linux/moduleparam.h>
3499a0b768STakashi Iwai #include <asm/io.h>
3599a0b768STakashi Iwai #include <asm/dma.h>
361da177e4SLinus Torvalds #include <sound/core.h>
371da177e4SLinus Torvalds #ifdef CS4231
381da177e4SLinus Torvalds #include <sound/cs4231.h>
391da177e4SLinus Torvalds #else
401da177e4SLinus Torvalds #ifndef OPTi93X
411da177e4SLinus Torvalds #include <sound/ad1848.h>
421da177e4SLinus Torvalds #else
431da177e4SLinus Torvalds #include <sound/control.h>
441da177e4SLinus Torvalds #include <sound/pcm.h>
451da177e4SLinus Torvalds #endif	/* OPTi93X */
461da177e4SLinus Torvalds #endif	/* CS4231 */
471da177e4SLinus Torvalds #include <sound/mpu401.h>
481da177e4SLinus Torvalds #include <sound/opl3.h>
491da177e4SLinus Torvalds #ifndef OPTi93X
501da177e4SLinus Torvalds #include <sound/opl4.h>
511da177e4SLinus Torvalds #endif
521da177e4SLinus Torvalds #define SNDRV_LEGACY_FIND_FREE_IRQ
531da177e4SLinus Torvalds #define SNDRV_LEGACY_FIND_FREE_DMA
541da177e4SLinus Torvalds #include <sound/initval.h>
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
571da177e4SLinus Torvalds MODULE_LICENSE("GPL");
581da177e4SLinus Torvalds #ifdef OPTi93X
591da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi93X");
601da177e4SLinus Torvalds MODULE_SUPPORTED_DEVICE("{{OPTi,82C931/3}}");
611da177e4SLinus Torvalds #else	/* OPTi93X */
621da177e4SLinus Torvalds #ifdef CS4231
631da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi92X - CS4231");
641da177e4SLinus Torvalds MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (CS4231)},"
651da177e4SLinus Torvalds 		"{OPTi,82C925 (CS4231)}}");
661da177e4SLinus Torvalds #else	/* CS4231 */
671da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi92X - AD1848");
681da177e4SLinus Torvalds MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)},"
691da177e4SLinus Torvalds 		"{OPTi,82C925 (AD1848)},"
701da177e4SLinus Torvalds 	        "{OAK,Mozart}}");
711da177e4SLinus Torvalds #endif	/* CS4231 */
721da177e4SLinus Torvalds #endif	/* OPTi93X */
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
751da177e4SLinus Torvalds static char *id = SNDRV_DEFAULT_STR1;		/* ID for this card */
761da177e4SLinus Torvalds //static int enable = SNDRV_DEFAULT_ENABLE1;	/* Enable this card */
771da177e4SLinus Torvalds static int isapnp = 1;			/* Enable ISA PnP detection */
781da177e4SLinus Torvalds static long port = SNDRV_DEFAULT_PORT1; 	/* 0x530,0xe80,0xf40,0x604 */
791da177e4SLinus Torvalds static long mpu_port = SNDRV_DEFAULT_PORT1;	/* 0x300,0x310,0x320,0x330 */
801da177e4SLinus Torvalds static long fm_port = SNDRV_DEFAULT_PORT1;	/* 0x388 */
811da177e4SLinus Torvalds static int irq = SNDRV_DEFAULT_IRQ1;		/* 5,7,9,10,11 */
821da177e4SLinus Torvalds static int mpu_irq = SNDRV_DEFAULT_IRQ1;	/* 5,7,9,10 */
831da177e4SLinus Torvalds static int dma1 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
841da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
851da177e4SLinus Torvalds static int dma2 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
861da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds module_param(index, int, 0444);
891da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for opti9xx based soundcard.");
901da177e4SLinus Torvalds module_param(id, charp, 0444);
911da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard.");
921da177e4SLinus Torvalds //module_param(enable, bool, 0444);
931da177e4SLinus Torvalds //MODULE_PARM_DESC(enable, "Enable opti9xx soundcard.");
941da177e4SLinus Torvalds module_param(isapnp, bool, 0444);
951da177e4SLinus Torvalds MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
961da177e4SLinus Torvalds module_param(port, long, 0444);
971da177e4SLinus Torvalds MODULE_PARM_DESC(port, "WSS port # for opti9xx driver.");
981da177e4SLinus Torvalds module_param(mpu_port, long, 0444);
991da177e4SLinus Torvalds MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver.");
1001da177e4SLinus Torvalds module_param(fm_port, long, 0444);
1011da177e4SLinus Torvalds MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver.");
1021da177e4SLinus Torvalds module_param(irq, int, 0444);
1031da177e4SLinus Torvalds MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver.");
1041da177e4SLinus Torvalds module_param(mpu_irq, int, 0444);
1051da177e4SLinus Torvalds MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver.");
1061da177e4SLinus Torvalds module_param(dma1, int, 0444);
1071da177e4SLinus Torvalds MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver.");
1081da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
1091da177e4SLinus Torvalds module_param(dma2, int, 0444);
1101da177e4SLinus Torvalds MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
1111da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds #define OPTi9XX_HW_DETECT	0
1141da177e4SLinus Torvalds #define OPTi9XX_HW_82C928	1
1151da177e4SLinus Torvalds #define OPTi9XX_HW_82C929	2
1161da177e4SLinus Torvalds #define OPTi9XX_HW_82C924	3
1171da177e4SLinus Torvalds #define OPTi9XX_HW_82C925	4
1181da177e4SLinus Torvalds #define OPTi9XX_HW_82C930	5
1191da177e4SLinus Torvalds #define OPTi9XX_HW_82C931	6
1201da177e4SLinus Torvalds #define OPTi9XX_HW_82C933	7
1211da177e4SLinus Torvalds #define OPTi9XX_HW_LAST		OPTi9XX_HW_82C933
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds #define OPTi9XX_MC_REG(n)	n
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds #ifdef OPTi93X
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds #define OPTi93X_INDEX			0x00
1281da177e4SLinus Torvalds #define OPTi93X_DATA			0x01
1291da177e4SLinus Torvalds #define OPTi93X_STATUS			0x02
1301da177e4SLinus Torvalds #define OPTi93X_DDATA			0x03
1311da177e4SLinus Torvalds #define OPTi93X_PORT(chip, r)		((chip)->port + OPTi93X_##r)
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds #define OPTi93X_MIXOUT_LEFT		0x00
1341da177e4SLinus Torvalds #define OPTi93X_MIXOUT_RIGHT		0x01
1351da177e4SLinus Torvalds #define OPTi93X_CD_LEFT_INPUT		0x02
1361da177e4SLinus Torvalds #define OPTi93X_CD_RIGHT_INPUT		0x03
1371da177e4SLinus Torvalds #define OPTi930_AUX_LEFT_INPUT		0x04
1381da177e4SLinus Torvalds #define OPTi930_AUX_RIGHT_INPUT		0x05
1391da177e4SLinus Torvalds #define OPTi931_FM_LEFT_INPUT		0x04
1401da177e4SLinus Torvalds #define OPTi931_FM_RIGHT_INPUT		0x05
1411da177e4SLinus Torvalds #define OPTi93X_DAC_LEFT		0x06
1421da177e4SLinus Torvalds #define OPTi93X_DAC_RIGHT		0x07
1431da177e4SLinus Torvalds #define OPTi93X_PLAY_FORMAT		0x08
1441da177e4SLinus Torvalds #define OPTi93X_IFACE_CONF		0x09
1451da177e4SLinus Torvalds #define OPTi93X_PIN_CTRL		0x0a
1461da177e4SLinus Torvalds #define OPTi93X_ERR_INIT		0x0b
1471da177e4SLinus Torvalds #define OPTi93X_ID			0x0c
1481da177e4SLinus Torvalds #define OPTi93X_PLAY_UPR_CNT		0x0e
1491da177e4SLinus Torvalds #define OPTi93X_PLAY_LWR_CNT		0x0f
1501da177e4SLinus Torvalds #define OPTi931_AUX_LEFT_INPUT		0x10
1511da177e4SLinus Torvalds #define OPTi931_AUX_RIGHT_INPUT		0x11
1521da177e4SLinus Torvalds #define OPTi93X_LINE_LEFT_INPUT		0x12
1531da177e4SLinus Torvalds #define OPTi93X_LINE_RIGHT_INPUT	0x13
1541da177e4SLinus Torvalds #define OPTi93X_MIC_LEFT_INPUT		0x14
1551da177e4SLinus Torvalds #define OPTi93X_MIC_RIGHT_INPUT		0x15
1561da177e4SLinus Torvalds #define OPTi93X_OUT_LEFT		0x16
1571da177e4SLinus Torvalds #define OPTi93X_OUT_RIGHT		0x17
1581da177e4SLinus Torvalds #define OPTi93X_CAPT_FORMAT		0x1c
1591da177e4SLinus Torvalds #define OPTi93X_CAPT_UPR_CNT		0x1e
1601da177e4SLinus Torvalds #define OPTi93X_CAPT_LWR_CNT		0x1f
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds #define OPTi93X_TRD			0x20
1631da177e4SLinus Torvalds #define OPTi93X_MCE			0x40
1641da177e4SLinus Torvalds #define OPTi93X_INIT			0x80
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds #define OPTi93X_MIXOUT_MIC_GAIN		0x20
1671da177e4SLinus Torvalds #define OPTi93X_MIXOUT_LINE		0x00
1681da177e4SLinus Torvalds #define OPTi93X_MIXOUT_CD		0x40
1691da177e4SLinus Torvalds #define OPTi93X_MIXOUT_MIC		0x80
1701da177e4SLinus Torvalds #define OPTi93X_MIXOUT_MIXER		0xc0
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds #define OPTi93X_STEREO			0x10
1731da177e4SLinus Torvalds #define OPTi93X_LINEAR_8		0x00
1741da177e4SLinus Torvalds #define OPTi93X_ULAW_8			0x20
1751da177e4SLinus Torvalds #define OPTi93X_LINEAR_16_LIT		0x40
1761da177e4SLinus Torvalds #define OPTi93X_ALAW_8			0x60
1771da177e4SLinus Torvalds #define OPTi93X_ADPCM_16		0xa0
1781da177e4SLinus Torvalds #define OPTi93X_LINEAR_16_BIG		0xc0
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds #define OPTi93X_CAPTURE_PIO		0x80
1811da177e4SLinus Torvalds #define OPTi93X_PLAYBACK_PIO		0x40
1821da177e4SLinus Torvalds #define OPTi93X_AUTOCALIB		0x08
1831da177e4SLinus Torvalds #define OPTi93X_SINGLE_DMA		0x04
1841da177e4SLinus Torvalds #define OPTi93X_CAPTURE_ENABLE		0x02
1851da177e4SLinus Torvalds #define OPTi93X_PLAYBACK_ENABLE		0x01
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds #define OPTi93X_IRQ_ENABLE		0x02
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds #define OPTi93X_DMA_REQUEST		0x10
1901da177e4SLinus Torvalds #define OPTi93X_CALIB_IN_PROGRESS	0x20
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds #define OPTi93X_IRQ_PLAYBACK		0x04
1931da177e4SLinus Torvalds #define OPTi93X_IRQ_CAPTURE		0x08
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds 
196346c7a68STakashi Iwai struct snd_opti93x {
1971da177e4SLinus Torvalds 	unsigned long port;
1981da177e4SLinus Torvalds 	struct resource *res_port;
1991da177e4SLinus Torvalds 	int irq;
2001da177e4SLinus Torvalds 	int dma1;
2011da177e4SLinus Torvalds 	int dma2;
2021da177e4SLinus Torvalds 
203346c7a68STakashi Iwai 	struct snd_opti9xx *chip;
2041da177e4SLinus Torvalds 	unsigned short hardware;
2051da177e4SLinus Torvalds 	unsigned char image[32];
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 	unsigned char mce_bit;
2081da177e4SLinus Torvalds 	unsigned short mode;
2091da177e4SLinus Torvalds 	int mute;
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds 	spinlock_t lock;
2121da177e4SLinus Torvalds 
213346c7a68STakashi Iwai 	struct snd_card *card;
214346c7a68STakashi Iwai 	struct snd_pcm *pcm;
215346c7a68STakashi Iwai 	struct snd_pcm_substream *playback_substream;
216346c7a68STakashi Iwai 	struct snd_pcm_substream *capture_substream;
2171da177e4SLinus Torvalds 	unsigned int p_dma_size;
2181da177e4SLinus Torvalds 	unsigned int c_dma_size;
2191da177e4SLinus Torvalds };
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds #define OPTi93X_MODE_NONE	0x00
2221da177e4SLinus Torvalds #define OPTi93X_MODE_PLAY	0x01
2231da177e4SLinus Torvalds #define OPTi93X_MODE_CAPTURE	0x02
2241da177e4SLinus Torvalds #define OPTi93X_MODE_OPEN	(OPTi93X_MODE_PLAY | OPTi93X_MODE_CAPTURE)
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds #endif /* OPTi93X */
2271da177e4SLinus Torvalds 
228346c7a68STakashi Iwai struct snd_opti9xx {
2291da177e4SLinus Torvalds 	unsigned short hardware;
2301da177e4SLinus Torvalds 	unsigned char password;
2311da177e4SLinus Torvalds 	char name[7];
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	unsigned long mc_base;
2341da177e4SLinus Torvalds 	struct resource *res_mc_base;
2351da177e4SLinus Torvalds 	unsigned long mc_base_size;
2361da177e4SLinus Torvalds #ifdef OPTi93X
2371da177e4SLinus Torvalds 	unsigned long mc_indir_index;
2381da177e4SLinus Torvalds #endif	/* OPTi93X */
2391da177e4SLinus Torvalds 	unsigned long pwd_reg;
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 	spinlock_t lock;
2421da177e4SLinus Torvalds 
2431da177e4SLinus Torvalds 	long wss_base;
2441da177e4SLinus Torvalds 	int irq;
2451da177e4SLinus Torvalds 	int dma1;
2461da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
2471da177e4SLinus Torvalds 	int dma2;
2481da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds 	long fm_port;
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds 	long mpu_port;
2531da177e4SLinus Torvalds 	int mpu_irq;
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds #ifdef CONFIG_PNP
2561da177e4SLinus Torvalds 	struct pnp_dev *dev;
2571da177e4SLinus Torvalds 	struct pnp_dev *devmpu;
2581da177e4SLinus Torvalds #endif	/* CONFIG_PNP */
2591da177e4SLinus Torvalds };
2601da177e4SLinus Torvalds 
26199a0b768STakashi Iwai static int snd_opti9xx_pnp_is_probed;
262f7a9275dSClemens Ladisch static struct platform_device *snd_opti9xx_platform_device;
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds #ifdef CONFIG_PNP
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds static struct pnp_card_device_id snd_opti9xx_pnpids[] = {
2671da177e4SLinus Torvalds #ifndef OPTi93X
2681da177e4SLinus Torvalds 	/* OPTi 82C924 */
2691da177e4SLinus Torvalds 	{ .id = "OPT0924", .devs = { { "OPT0000" }, { "OPT0002" } }, .driver_data = 0x0924 },
2701da177e4SLinus Torvalds 	/* OPTi 82C925 */
2711da177e4SLinus Torvalds 	{ .id = "OPT0925", .devs = { { "OPT9250" }, { "OPT0002" } }, .driver_data = 0x0925 },
2721da177e4SLinus Torvalds #else
2731da177e4SLinus Torvalds 	/* OPTi 82C931/3 */
2741da177e4SLinus Torvalds 	{ .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } }, .driver_data = 0x0931 },
2751da177e4SLinus Torvalds #endif	/* OPTi93X */
2761da177e4SLinus Torvalds 	{ .id = "" }
2771da177e4SLinus Torvalds };
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids);
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds #endif	/* CONFIG_PNP */
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds #ifdef OPTi93X
2841da177e4SLinus Torvalds #define DRIVER_NAME	"snd-card-opti93x"
2851da177e4SLinus Torvalds #else
2861da177e4SLinus Torvalds #define DRIVER_NAME	"snd-card-opti92x"
2871da177e4SLinus Torvalds #endif	/* OPTi93X */
2881da177e4SLinus Torvalds 
2891da177e4SLinus Torvalds static char * snd_opti9xx_names[] = {
2901da177e4SLinus Torvalds 	"unkown",
2911da177e4SLinus Torvalds 	"82C928",	"82C929",
2921da177e4SLinus Torvalds 	"82C924",	"82C925",
2931da177e4SLinus Torvalds 	"82C930",	"82C931",	"82C933"
2941da177e4SLinus Torvalds };
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds 
29799a0b768STakashi Iwai static long __init snd_legacy_find_free_ioport(long *port_table, long size)
2981da177e4SLinus Torvalds {
2991da177e4SLinus Torvalds 	while (*port_table != -1) {
300b1d5776dSTakashi Iwai 		if (request_region(*port_table, size, "ALSA test")) {
301b1d5776dSTakashi Iwai 			release_region(*port_table, size);
3021da177e4SLinus Torvalds 			return *port_table;
3031da177e4SLinus Torvalds 		}
3041da177e4SLinus Torvalds 		port_table++;
3051da177e4SLinus Torvalds 	}
3061da177e4SLinus Torvalds 	return -1;
3071da177e4SLinus Torvalds }
3081da177e4SLinus Torvalds 
30999a0b768STakashi Iwai static int __init snd_opti9xx_init(struct snd_opti9xx *chip, unsigned short hardware)
3101da177e4SLinus Torvalds {
3111da177e4SLinus Torvalds 	static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	chip->hardware = hardware;
3141da177e4SLinus Torvalds 	strcpy(chip->name, snd_opti9xx_names[hardware]);
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds 	chip->mc_base_size = opti9xx_mc_size[hardware];
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds 	spin_lock_init(&chip->lock);
3191da177e4SLinus Torvalds 
3201da177e4SLinus Torvalds 	chip->wss_base = -1;
3211da177e4SLinus Torvalds 	chip->irq = -1;
3221da177e4SLinus Torvalds 	chip->dma1 = -1;
3231da177e4SLinus Torvalds #if defined(CS4231) || defined (OPTi93X)
3241da177e4SLinus Torvalds 	chip->dma2 = -1;
3251da177e4SLinus Torvalds #endif 	/* CS4231 || OPTi93X */
3261da177e4SLinus Torvalds 	chip->fm_port = -1;
3271da177e4SLinus Torvalds 	chip->mpu_port = -1;
3281da177e4SLinus Torvalds 	chip->mpu_irq = -1;
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds 	switch (hardware) {
3311da177e4SLinus Torvalds #ifndef OPTi93X
3321da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
3331da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
3341da177e4SLinus Torvalds 		chip->mc_base = 0xf8c;
3351da177e4SLinus Torvalds 		chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3;
3361da177e4SLinus Torvalds 		chip->pwd_reg = 3;
3371da177e4SLinus Torvalds 		break;
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
3401da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
3411da177e4SLinus Torvalds 		chip->mc_base = 0xf8c;
3421da177e4SLinus Torvalds 		chip->password = 0xe5;
3431da177e4SLinus Torvalds 		chip->pwd_reg = 3;
3441da177e4SLinus Torvalds 		break;
3451da177e4SLinus Torvalds #else	/* OPTi93X */
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930:
3481da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
3491da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
3501da177e4SLinus Torvalds 		chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d;
3511da177e4SLinus Torvalds 		chip->mc_indir_index = 0xe0e;
3521da177e4SLinus Torvalds 		chip->password = 0xe4;
3531da177e4SLinus Torvalds 		chip->pwd_reg = 0;
3541da177e4SLinus Torvalds 		break;
3551da177e4SLinus Torvalds #endif	/* OPTi93X */
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds 	default:
3581da177e4SLinus Torvalds 		snd_printk("chip %d not supported\n", hardware);
3591da177e4SLinus Torvalds 		return -ENODEV;
3601da177e4SLinus Torvalds 	}
3611da177e4SLinus Torvalds 	return 0;
3621da177e4SLinus Torvalds }
3631da177e4SLinus Torvalds 
364346c7a68STakashi Iwai static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,
3651da177e4SLinus Torvalds 				      unsigned char reg)
3661da177e4SLinus Torvalds {
3671da177e4SLinus Torvalds 	unsigned long flags;
3681da177e4SLinus Torvalds 	unsigned char retval = 0xff;
3691da177e4SLinus Torvalds 
3701da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
3711da177e4SLinus Torvalds 	outb(chip->password, chip->mc_base + chip->pwd_reg);
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds 	switch (chip->hardware) {
3741da177e4SLinus Torvalds #ifndef OPTi93X
3751da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
3761da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
3771da177e4SLinus Torvalds 		if (reg > 7) {
3781da177e4SLinus Torvalds 			outb(reg, chip->mc_base + 8);
3791da177e4SLinus Torvalds 			outb(chip->password, chip->mc_base + chip->pwd_reg);
3801da177e4SLinus Torvalds 			retval = inb(chip->mc_base + 9);
3811da177e4SLinus Torvalds 			break;
3821da177e4SLinus Torvalds 		}
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
3851da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
3861da177e4SLinus Torvalds 		retval = inb(chip->mc_base + reg);
3871da177e4SLinus Torvalds 		break;
3881da177e4SLinus Torvalds #else	/* OPTi93X */
3891da177e4SLinus Torvalds 
3901da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930:
3911da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
3921da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
3931da177e4SLinus Torvalds 		outb(reg, chip->mc_indir_index);
3941da177e4SLinus Torvalds 		outb(chip->password, chip->mc_base + chip->pwd_reg);
3951da177e4SLinus Torvalds 		retval = inb(chip->mc_indir_index + 1);
3961da177e4SLinus Torvalds 		break;
3971da177e4SLinus Torvalds #endif	/* OPTi93X */
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds 	default:
4001da177e4SLinus Torvalds 		snd_printk("chip %d not supported\n", chip->hardware);
4011da177e4SLinus Torvalds 	}
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
4041da177e4SLinus Torvalds 	return retval;
4051da177e4SLinus Torvalds }
4061da177e4SLinus Torvalds 
407346c7a68STakashi Iwai static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
4081da177e4SLinus Torvalds 			      unsigned char value)
4091da177e4SLinus Torvalds {
4101da177e4SLinus Torvalds 	unsigned long flags;
4111da177e4SLinus Torvalds 
4121da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
4131da177e4SLinus Torvalds 	outb(chip->password, chip->mc_base + chip->pwd_reg);
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds 	switch (chip->hardware) {
4161da177e4SLinus Torvalds #ifndef OPTi93X
4171da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
4181da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
4191da177e4SLinus Torvalds 		if (reg > 7) {
4201da177e4SLinus Torvalds 			outb(reg, chip->mc_base + 8);
4211da177e4SLinus Torvalds 			outb(chip->password, chip->mc_base + chip->pwd_reg);
4221da177e4SLinus Torvalds 			outb(value, chip->mc_base + 9);
4231da177e4SLinus Torvalds 			break;
4241da177e4SLinus Torvalds 		}
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
4271da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
4281da177e4SLinus Torvalds 		outb(value, chip->mc_base + reg);
4291da177e4SLinus Torvalds 		break;
4301da177e4SLinus Torvalds #else	/* OPTi93X */
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930:
4331da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
4341da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
4351da177e4SLinus Torvalds 		outb(reg, chip->mc_indir_index);
4361da177e4SLinus Torvalds 		outb(chip->password, chip->mc_base + chip->pwd_reg);
4371da177e4SLinus Torvalds 		outb(value, chip->mc_indir_index + 1);
4381da177e4SLinus Torvalds 		break;
4391da177e4SLinus Torvalds #endif	/* OPTi93X */
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds 	default:
4421da177e4SLinus Torvalds 		snd_printk("chip %d not supported\n", chip->hardware);
4431da177e4SLinus Torvalds 	}
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
4461da177e4SLinus Torvalds }
4471da177e4SLinus Torvalds 
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds #define snd_opti9xx_write_mask(chip, reg, value, mask)	\
4501da177e4SLinus Torvalds 	snd_opti9xx_write(chip, reg,			\
4511da177e4SLinus Torvalds 		(snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))
4521da177e4SLinus Torvalds 
4531da177e4SLinus Torvalds 
45499a0b768STakashi Iwai static int __init snd_opti9xx_configure(struct snd_opti9xx *chip)
4551da177e4SLinus Torvalds {
4561da177e4SLinus Torvalds 	unsigned char wss_base_bits;
4571da177e4SLinus Torvalds 	unsigned char irq_bits;
4581da177e4SLinus Torvalds 	unsigned char dma_bits;
4591da177e4SLinus Torvalds 	unsigned char mpu_port_bits = 0;
4601da177e4SLinus Torvalds 	unsigned char mpu_irq_bits;
4611da177e4SLinus Torvalds 
4621da177e4SLinus Torvalds 	switch (chip->hardware) {
4631da177e4SLinus Torvalds #ifndef OPTi93X
4641da177e4SLinus Torvalds 	case OPTi9XX_HW_82C924:
4651da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc);
4661da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
4671da177e4SLinus Torvalds 
4681da177e4SLinus Torvalds 	case OPTi9XX_HW_82C925:
4691da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
4701da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
4711da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
4721da177e4SLinus Torvalds #ifdef CS4231
4731da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
4741da177e4SLinus Torvalds #else
4751da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
4761da177e4SLinus Torvalds #endif	/* CS4231 */
4771da177e4SLinus Torvalds 		break;
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds 	case OPTi9XX_HW_82C928:
4801da177e4SLinus Torvalds 	case OPTi9XX_HW_82C929:
4811da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
4821da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
4831da177e4SLinus Torvalds 		/*
4841da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae);
4851da177e4SLinus Torvalds 		*/
4861da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
4871da177e4SLinus Torvalds #ifdef CS4231
4881da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
4891da177e4SLinus Torvalds #else
4901da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
4911da177e4SLinus Torvalds #endif	/* CS4231 */
4921da177e4SLinus Torvalds 		break;
4931da177e4SLinus Torvalds 
4941da177e4SLinus Torvalds #else	/* OPTi93X */
4951da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930:
4961da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
4971da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
4981da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
4991da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
5001da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
5011da177e4SLinus Torvalds 			(chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
5021da177e4SLinus Torvalds 			0x34);
5031da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
5041da177e4SLinus Torvalds 		break;
5051da177e4SLinus Torvalds #endif	/* OPTi93X */
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds 	default:
5081da177e4SLinus Torvalds 		snd_printk("chip %d not supported\n", chip->hardware);
5091da177e4SLinus Torvalds 		return -EINVAL;
5101da177e4SLinus Torvalds 	}
5111da177e4SLinus Torvalds 
5121da177e4SLinus Torvalds 	switch (chip->wss_base) {
5131da177e4SLinus Torvalds 	case 0x530:
5141da177e4SLinus Torvalds 		wss_base_bits = 0x00;
5151da177e4SLinus Torvalds 		break;
5161da177e4SLinus Torvalds 	case 0x604:
5171da177e4SLinus Torvalds 		wss_base_bits = 0x03;
5181da177e4SLinus Torvalds 		break;
5191da177e4SLinus Torvalds 	case 0xe80:
5201da177e4SLinus Torvalds 		wss_base_bits = 0x01;
5211da177e4SLinus Torvalds 		break;
5221da177e4SLinus Torvalds 	case 0xf40:
5231da177e4SLinus Torvalds 		wss_base_bits = 0x02;
5241da177e4SLinus Torvalds 		break;
5251da177e4SLinus Torvalds 	default:
5261da177e4SLinus Torvalds 		snd_printk("WSS port 0x%lx not valid\n", chip->wss_base);
5271da177e4SLinus Torvalds 		goto __skip_base;
5281da177e4SLinus Torvalds 	}
5291da177e4SLinus Torvalds 	snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
5301da177e4SLinus Torvalds 
5311da177e4SLinus Torvalds __skip_base:
5321da177e4SLinus Torvalds 	switch (chip->irq) {
5331da177e4SLinus Torvalds //#ifdef OPTi93X
5341da177e4SLinus Torvalds 	case 5:
5351da177e4SLinus Torvalds 		irq_bits = 0x05;
5361da177e4SLinus Torvalds 		break;
5371da177e4SLinus Torvalds //#endif	/* OPTi93X */
5381da177e4SLinus Torvalds 	case 7:
5391da177e4SLinus Torvalds 		irq_bits = 0x01;
5401da177e4SLinus Torvalds 		break;
5411da177e4SLinus Torvalds 	case 9:
5421da177e4SLinus Torvalds 		irq_bits = 0x02;
5431da177e4SLinus Torvalds 		break;
5441da177e4SLinus Torvalds 	case 10:
5451da177e4SLinus Torvalds 		irq_bits = 0x03;
5461da177e4SLinus Torvalds 		break;
5471da177e4SLinus Torvalds 	case 11:
5481da177e4SLinus Torvalds 		irq_bits = 0x04;
5491da177e4SLinus Torvalds 		break;
5501da177e4SLinus Torvalds 	default:
5511da177e4SLinus Torvalds 		snd_printk("WSS irq # %d not valid\n", chip->irq);
5521da177e4SLinus Torvalds 		goto __skip_resources;
5531da177e4SLinus Torvalds 	}
5541da177e4SLinus Torvalds 
5551da177e4SLinus Torvalds 	switch (chip->dma1) {
5561da177e4SLinus Torvalds 	case 0:
5571da177e4SLinus Torvalds 		dma_bits = 0x01;
5581da177e4SLinus Torvalds 		break;
5591da177e4SLinus Torvalds 	case 1:
5601da177e4SLinus Torvalds 		dma_bits = 0x02;
5611da177e4SLinus Torvalds 		break;
5621da177e4SLinus Torvalds 	case 3:
5631da177e4SLinus Torvalds 		dma_bits = 0x03;
5641da177e4SLinus Torvalds 		break;
5651da177e4SLinus Torvalds 	default:
5661da177e4SLinus Torvalds 		snd_printk("WSS dma1 # %d not valid\n", chip->dma1);
5671da177e4SLinus Torvalds 		goto __skip_resources;
5681da177e4SLinus Torvalds 	}
5691da177e4SLinus Torvalds 
5701da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
5711da177e4SLinus Torvalds 	if (chip->dma1 == chip->dma2) {
5721da177e4SLinus Torvalds 		snd_printk("don't want to share dmas\n");
5731da177e4SLinus Torvalds 		return -EBUSY;
5741da177e4SLinus Torvalds 	}
5751da177e4SLinus Torvalds 
5761da177e4SLinus Torvalds 	switch (chip->dma2) {
5771da177e4SLinus Torvalds 	case 0:
5781da177e4SLinus Torvalds 	case 1:
5791da177e4SLinus Torvalds 		break;
5801da177e4SLinus Torvalds 	default:
5811da177e4SLinus Torvalds 		snd_printk("WSS dma2 # %d not valid\n", chip->dma2);
5821da177e4SLinus Torvalds 		goto __skip_resources;
5831da177e4SLinus Torvalds 	}
5841da177e4SLinus Torvalds 	dma_bits |= 0x04;
5851da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
5861da177e4SLinus Torvalds 
5871da177e4SLinus Torvalds #ifndef OPTi93X
5881da177e4SLinus Torvalds 	 outb(irq_bits << 3 | dma_bits, chip->wss_base);
5891da177e4SLinus Torvalds #else /* OPTi93X */
5901da177e4SLinus Torvalds 	snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));
5911da177e4SLinus Torvalds #endif /* OPTi93X */
5921da177e4SLinus Torvalds 
5931da177e4SLinus Torvalds __skip_resources:
5941da177e4SLinus Torvalds 	if (chip->hardware > OPTi9XX_HW_82C928) {
5951da177e4SLinus Torvalds 		switch (chip->mpu_port) {
5961da177e4SLinus Torvalds 		case 0:
5971da177e4SLinus Torvalds 		case -1:
5981da177e4SLinus Torvalds 			break;
5991da177e4SLinus Torvalds 		case 0x300:
6001da177e4SLinus Torvalds 			mpu_port_bits = 0x03;
6011da177e4SLinus Torvalds 			break;
6021da177e4SLinus Torvalds 		case 0x310:
6031da177e4SLinus Torvalds 			mpu_port_bits = 0x02;
6041da177e4SLinus Torvalds 			break;
6051da177e4SLinus Torvalds 		case 0x320:
6061da177e4SLinus Torvalds 			mpu_port_bits = 0x01;
6071da177e4SLinus Torvalds 			break;
6081da177e4SLinus Torvalds 		case 0x330:
6091da177e4SLinus Torvalds 			mpu_port_bits = 0x00;
6101da177e4SLinus Torvalds 			break;
6111da177e4SLinus Torvalds 		default:
6121da177e4SLinus Torvalds 			snd_printk("MPU-401 port 0x%lx not valid\n",
6131da177e4SLinus Torvalds 				chip->mpu_port);
6141da177e4SLinus Torvalds 			goto __skip_mpu;
6151da177e4SLinus Torvalds 		}
6161da177e4SLinus Torvalds 
6171da177e4SLinus Torvalds 		switch (chip->mpu_irq) {
6181da177e4SLinus Torvalds 		case 5:
6191da177e4SLinus Torvalds 			mpu_irq_bits = 0x02;
6201da177e4SLinus Torvalds 			break;
6211da177e4SLinus Torvalds 		case 7:
6221da177e4SLinus Torvalds 			mpu_irq_bits = 0x03;
6231da177e4SLinus Torvalds 			break;
6241da177e4SLinus Torvalds 		case 9:
6251da177e4SLinus Torvalds 			mpu_irq_bits = 0x00;
6261da177e4SLinus Torvalds 			break;
6271da177e4SLinus Torvalds 		case 10:
6281da177e4SLinus Torvalds 			mpu_irq_bits = 0x01;
6291da177e4SLinus Torvalds 			break;
6301da177e4SLinus Torvalds 		default:
6311da177e4SLinus Torvalds 			snd_printk("MPU-401 irq # %d not valid\n",
6321da177e4SLinus Torvalds 				chip->mpu_irq);
6331da177e4SLinus Torvalds 			goto __skip_mpu;
6341da177e4SLinus Torvalds 		}
6351da177e4SLinus Torvalds 
6361da177e4SLinus Torvalds 		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6),
6371da177e4SLinus Torvalds 			(chip->mpu_port <= 0) ? 0x00 :
6381da177e4SLinus Torvalds 				0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
6391da177e4SLinus Torvalds 			0xf8);
6401da177e4SLinus Torvalds 	}
6411da177e4SLinus Torvalds __skip_mpu:
6421da177e4SLinus Torvalds 
6431da177e4SLinus Torvalds 	return 0;
6441da177e4SLinus Torvalds }
6451da177e4SLinus Torvalds 
6461da177e4SLinus Torvalds #ifdef OPTi93X
6471da177e4SLinus Torvalds 
6481da177e4SLinus Torvalds static unsigned char snd_opti93x_default_image[32] =
6491da177e4SLinus Torvalds {
6501da177e4SLinus Torvalds 	0x00,		/* 00/00 - l_mixout_outctrl */
6511da177e4SLinus Torvalds 	0x00,		/* 01/01 - r_mixout_outctrl */
6521da177e4SLinus Torvalds 	0x88,		/* 02/02 - l_cd_inctrl */
6531da177e4SLinus Torvalds 	0x88,		/* 03/03 - r_cd_inctrl */
6541da177e4SLinus Torvalds 	0x88,		/* 04/04 - l_a1/fm_inctrl */
6551da177e4SLinus Torvalds 	0x88,		/* 05/05 - r_a1/fm_inctrl */
6561da177e4SLinus Torvalds 	0x80,		/* 06/06 - l_dac_inctrl */
6571da177e4SLinus Torvalds 	0x80,		/* 07/07 - r_dac_inctrl */
6581da177e4SLinus Torvalds 	0x00,		/* 08/08 - ply_dataform_reg */
6591da177e4SLinus Torvalds 	0x00,		/* 09/09 - if_conf */
6601da177e4SLinus Torvalds 	0x00,		/* 0a/10 - pin_ctrl */
6611da177e4SLinus Torvalds 	0x00,		/* 0b/11 - err_init_reg */
6621da177e4SLinus Torvalds 	0x0a,		/* 0c/12 - id_reg */
6631da177e4SLinus Torvalds 	0x00,		/* 0d/13 - reserved */
6641da177e4SLinus Torvalds 	0x00,		/* 0e/14 - ply_upcount_reg */
6651da177e4SLinus Torvalds 	0x00,		/* 0f/15 - ply_lowcount_reg */
6661da177e4SLinus Torvalds 	0x88,		/* 10/16 - reserved/l_a1_inctrl */
6671da177e4SLinus Torvalds 	0x88,		/* 11/17 - reserved/r_a1_inctrl */
6681da177e4SLinus Torvalds 	0x88,		/* 12/18 - l_line_inctrl */
6691da177e4SLinus Torvalds 	0x88,		/* 13/19 - r_line_inctrl */
6701da177e4SLinus Torvalds 	0x88,		/* 14/20 - l_mic_inctrl */
6711da177e4SLinus Torvalds 	0x88,		/* 15/21 - r_mic_inctrl */
6721da177e4SLinus Torvalds 	0x80,		/* 16/22 - l_out_outctrl */
6731da177e4SLinus Torvalds 	0x80,		/* 17/23 - r_out_outctrl */
6741da177e4SLinus Torvalds 	0x00,		/* 18/24 - reserved */
6751da177e4SLinus Torvalds 	0x00,		/* 19/25 - reserved */
6761da177e4SLinus Torvalds 	0x00,		/* 1a/26 - reserved */
6771da177e4SLinus Torvalds 	0x00,		/* 1b/27 - reserved */
6781da177e4SLinus Torvalds 	0x00,		/* 1c/28 - cap_dataform_reg */
6791da177e4SLinus Torvalds 	0x00,		/* 1d/29 - reserved */
6801da177e4SLinus Torvalds 	0x00,		/* 1e/30 - cap_upcount_reg */
6811da177e4SLinus Torvalds 	0x00		/* 1f/31 - cap_lowcount_reg */
6821da177e4SLinus Torvalds };
6831da177e4SLinus Torvalds 
6841da177e4SLinus Torvalds 
685346c7a68STakashi Iwai static int snd_opti93x_busy_wait(struct snd_opti93x *chip)
6861da177e4SLinus Torvalds {
6871da177e4SLinus Torvalds 	int timeout;
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds 	for (timeout = 250; timeout-- > 0; udelay(10))
6901da177e4SLinus Torvalds 		if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_INIT))
6911da177e4SLinus Torvalds 			return 0;
6921da177e4SLinus Torvalds 
6931da177e4SLinus Torvalds 	snd_printk("chip still busy.\n");
6941da177e4SLinus Torvalds 	return -EBUSY;
6951da177e4SLinus Torvalds }
6961da177e4SLinus Torvalds 
697346c7a68STakashi Iwai static unsigned char snd_opti93x_in(struct snd_opti93x *chip, unsigned char reg)
6981da177e4SLinus Torvalds {
6991da177e4SLinus Torvalds 	snd_opti93x_busy_wait(chip);
7001da177e4SLinus Torvalds 	outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX));
7011da177e4SLinus Torvalds 	return inb(OPTi93X_PORT(chip, DATA));
7021da177e4SLinus Torvalds }
7031da177e4SLinus Torvalds 
704346c7a68STakashi Iwai static void snd_opti93x_out(struct snd_opti93x *chip, unsigned char reg,
7051da177e4SLinus Torvalds 			    unsigned char value)
7061da177e4SLinus Torvalds {
7071da177e4SLinus Torvalds 	snd_opti93x_busy_wait(chip);
7081da177e4SLinus Torvalds 	outb(chip->mce_bit | (reg & 0x1f), OPTi93X_PORT(chip, INDEX));
7091da177e4SLinus Torvalds 	outb(value, OPTi93X_PORT(chip, DATA));
7101da177e4SLinus Torvalds }
7111da177e4SLinus Torvalds 
712346c7a68STakashi Iwai static void snd_opti93x_out_image(struct snd_opti93x *chip, unsigned char reg,
7131da177e4SLinus Torvalds 				  unsigned char value)
7141da177e4SLinus Torvalds {
7151da177e4SLinus Torvalds 	snd_opti93x_out(chip, reg, chip->image[reg] = value);
7161da177e4SLinus Torvalds }
7171da177e4SLinus Torvalds 
718346c7a68STakashi Iwai static void snd_opti93x_out_mask(struct snd_opti93x *chip, unsigned char reg,
7191da177e4SLinus Torvalds 				 unsigned char mask, unsigned char value)
7201da177e4SLinus Torvalds {
7211da177e4SLinus Torvalds 	snd_opti93x_out_image(chip, reg,
7221da177e4SLinus Torvalds 		(chip->image[reg] & ~mask) | (value & mask));
7231da177e4SLinus Torvalds }
7241da177e4SLinus Torvalds 
7251da177e4SLinus Torvalds 
726346c7a68STakashi Iwai static void snd_opti93x_mce_up(struct snd_opti93x *chip)
7271da177e4SLinus Torvalds {
7281da177e4SLinus Torvalds 	snd_opti93x_busy_wait(chip);
7291da177e4SLinus Torvalds 
7301da177e4SLinus Torvalds 	chip->mce_bit = OPTi93X_MCE;
7311da177e4SLinus Torvalds 	if (!(inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE))
7321da177e4SLinus Torvalds 		outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX));
7331da177e4SLinus Torvalds }
7341da177e4SLinus Torvalds 
735346c7a68STakashi Iwai static void snd_opti93x_mce_down(struct snd_opti93x *chip)
7361da177e4SLinus Torvalds {
7371da177e4SLinus Torvalds 	snd_opti93x_busy_wait(chip);
7381da177e4SLinus Torvalds 
7391da177e4SLinus Torvalds 	chip->mce_bit = 0;
7401da177e4SLinus Torvalds 	if (inb(OPTi93X_PORT(chip, INDEX)) & OPTi93X_MCE)
7411da177e4SLinus Torvalds 		outb(chip->mce_bit, OPTi93X_PORT(chip, INDEX));
7421da177e4SLinus Torvalds }
7431da177e4SLinus Torvalds 
7441da177e4SLinus Torvalds #define snd_opti93x_mute_reg(chip, reg, mute)	\
7451da177e4SLinus Torvalds 	snd_opti93x_out(chip, reg, mute ? 0x80 : chip->image[reg]);
7461da177e4SLinus Torvalds 
747346c7a68STakashi Iwai static void snd_opti93x_mute(struct snd_opti93x *chip, int mute)
7481da177e4SLinus Torvalds {
7491da177e4SLinus Torvalds 	mute = mute ? 1 : 0;
7501da177e4SLinus Torvalds 	if (chip->mute == mute)
7511da177e4SLinus Torvalds 		return;
7521da177e4SLinus Torvalds 
7531da177e4SLinus Torvalds 	chip->mute = mute;
7541da177e4SLinus Torvalds 
7551da177e4SLinus Torvalds 	snd_opti93x_mute_reg(chip, OPTi93X_CD_LEFT_INPUT, mute);
7561da177e4SLinus Torvalds 	snd_opti93x_mute_reg(chip, OPTi93X_CD_RIGHT_INPUT, mute);
7571da177e4SLinus Torvalds 	switch (chip->hardware) {
7581da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930:
7591da177e4SLinus Torvalds 		snd_opti93x_mute_reg(chip, OPTi930_AUX_LEFT_INPUT, mute);
7601da177e4SLinus Torvalds 		snd_opti93x_mute_reg(chip, OPTi930_AUX_RIGHT_INPUT, mute);
7611da177e4SLinus Torvalds 		break;
7621da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931:
7631da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933:
7641da177e4SLinus Torvalds 		snd_opti93x_mute_reg(chip, OPTi931_FM_LEFT_INPUT, mute);
7651da177e4SLinus Torvalds 		snd_opti93x_mute_reg(chip, OPTi931_FM_RIGHT_INPUT, mute);
7661da177e4SLinus Torvalds 		snd_opti93x_mute_reg(chip, OPTi931_AUX_LEFT_INPUT, mute);
7671da177e4SLinus Torvalds 		snd_opti93x_mute_reg(chip, OPTi931_AUX_RIGHT_INPUT, mute);
7681da177e4SLinus Torvalds 	}
7691da177e4SLinus Torvalds 	snd_opti93x_mute_reg(chip, OPTi93X_DAC_LEFT, mute);
7701da177e4SLinus Torvalds 	snd_opti93x_mute_reg(chip, OPTi93X_DAC_RIGHT, mute);
7711da177e4SLinus Torvalds 	snd_opti93x_mute_reg(chip, OPTi93X_LINE_LEFT_INPUT, mute);
7721da177e4SLinus Torvalds 	snd_opti93x_mute_reg(chip, OPTi93X_LINE_RIGHT_INPUT, mute);
7731da177e4SLinus Torvalds 	snd_opti93x_mute_reg(chip, OPTi93X_MIC_LEFT_INPUT, mute);
7741da177e4SLinus Torvalds 	snd_opti93x_mute_reg(chip, OPTi93X_MIC_RIGHT_INPUT, mute);
7751da177e4SLinus Torvalds 	snd_opti93x_mute_reg(chip, OPTi93X_OUT_LEFT, mute);
7761da177e4SLinus Torvalds 	snd_opti93x_mute_reg(chip, OPTi93X_OUT_RIGHT, mute);
7771da177e4SLinus Torvalds }
7781da177e4SLinus Torvalds 
7791da177e4SLinus Torvalds 
7801da177e4SLinus Torvalds static unsigned int snd_opti93x_get_count(unsigned char format,
7811da177e4SLinus Torvalds 					  unsigned int size)
7821da177e4SLinus Torvalds {
7831da177e4SLinus Torvalds 	switch (format & 0xe0) {
7841da177e4SLinus Torvalds 	case OPTi93X_LINEAR_16_LIT:
7851da177e4SLinus Torvalds 	case OPTi93X_LINEAR_16_BIG:
7861da177e4SLinus Torvalds 		size >>= 1;
7871da177e4SLinus Torvalds 		break;
7881da177e4SLinus Torvalds 	case OPTi93X_ADPCM_16:
7891da177e4SLinus Torvalds 		return size >> 2;
7901da177e4SLinus Torvalds 	}
7911da177e4SLinus Torvalds 	return (format & OPTi93X_STEREO) ? (size >> 1) : size;
7921da177e4SLinus Torvalds }
7931da177e4SLinus Torvalds 
7941da177e4SLinus Torvalds static unsigned int rates[] = {  5512,  6615,  8000,  9600, 11025, 16000,
7951da177e4SLinus Torvalds 				18900, 22050, 27428, 32000, 33075, 37800,
7961da177e4SLinus Torvalds 				44100, 48000 };
7971da177e4SLinus Torvalds #define RATES ARRAY_SIZE(rates)
7981da177e4SLinus Torvalds 
799346c7a68STakashi Iwai static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
8001da177e4SLinus Torvalds 	.count = RATES,
8011da177e4SLinus Torvalds 	.list = rates,
8021da177e4SLinus Torvalds 	.mask = 0,
8031da177e4SLinus Torvalds };
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds static unsigned char bits[] = {  0x01,  0x0f,  0x00,  0x0e,  0x03,  0x02,
8061da177e4SLinus Torvalds 				 0x05,  0x07,  0x04,  0x06,  0x0d,  0x09,
8071da177e4SLinus Torvalds 				 0x0b,  0x0c};
8081da177e4SLinus Torvalds 
8091da177e4SLinus Torvalds static unsigned char snd_opti93x_get_freq(unsigned int rate)
8101da177e4SLinus Torvalds {
8111da177e4SLinus Torvalds 	unsigned int i;
8121da177e4SLinus Torvalds 
8131da177e4SLinus Torvalds 	for (i = 0; i < RATES; i++) {
8141da177e4SLinus Torvalds 		if (rate == rates[i])
8151da177e4SLinus Torvalds 			return bits[i];
8161da177e4SLinus Torvalds 	}
8171da177e4SLinus Torvalds 	snd_BUG();
8181da177e4SLinus Torvalds 	return bits[RATES-1];
8191da177e4SLinus Torvalds }
8201da177e4SLinus Torvalds 
821346c7a68STakashi Iwai static unsigned char snd_opti93x_get_format(struct snd_opti93x *chip,
8221da177e4SLinus Torvalds 					    unsigned int format, int channels)
8231da177e4SLinus Torvalds {
8241da177e4SLinus Torvalds 	unsigned char retval = OPTi93X_LINEAR_8;
8251da177e4SLinus Torvalds 
8261da177e4SLinus Torvalds 	switch (format) {
8271da177e4SLinus Torvalds 	case SNDRV_PCM_FORMAT_MU_LAW:
8281da177e4SLinus Torvalds 		retval = OPTi93X_ULAW_8;
8291da177e4SLinus Torvalds 		break;
8301da177e4SLinus Torvalds 	case SNDRV_PCM_FORMAT_A_LAW:
8311da177e4SLinus Torvalds 		retval = OPTi93X_ALAW_8;
8321da177e4SLinus Torvalds 		break;
8331da177e4SLinus Torvalds 	case SNDRV_PCM_FORMAT_S16_LE:
8341da177e4SLinus Torvalds 		retval = OPTi93X_LINEAR_16_LIT;
8351da177e4SLinus Torvalds 		break;
8361da177e4SLinus Torvalds 	case SNDRV_PCM_FORMAT_S16_BE:
8371da177e4SLinus Torvalds 		retval = OPTi93X_LINEAR_16_BIG;
8381da177e4SLinus Torvalds 		break;
8391da177e4SLinus Torvalds 	case SNDRV_PCM_FORMAT_IMA_ADPCM:
8401da177e4SLinus Torvalds 		retval = OPTi93X_ADPCM_16;
8411da177e4SLinus Torvalds 	}
8421da177e4SLinus Torvalds 	return (channels > 1) ? (retval | OPTi93X_STEREO) : retval;
8431da177e4SLinus Torvalds }
8441da177e4SLinus Torvalds 
8451da177e4SLinus Torvalds 
846346c7a68STakashi Iwai static void snd_opti93x_playback_format(struct snd_opti93x *chip, unsigned char fmt)
8471da177e4SLinus Torvalds {
8481da177e4SLinus Torvalds 	unsigned char mask;
8491da177e4SLinus Torvalds 
8501da177e4SLinus Torvalds 	snd_opti93x_mute(chip, 1);
8511da177e4SLinus Torvalds 
8521da177e4SLinus Torvalds 	snd_opti93x_mce_up(chip);
8531da177e4SLinus Torvalds 	mask = (chip->mode & OPTi93X_MODE_CAPTURE) ? 0xf0 : 0xff;
8541da177e4SLinus Torvalds 	snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, mask, fmt);
8551da177e4SLinus Torvalds 	snd_opti93x_mce_down(chip);
8561da177e4SLinus Torvalds 
8571da177e4SLinus Torvalds 	snd_opti93x_mute(chip, 0);
8581da177e4SLinus Torvalds }
8591da177e4SLinus Torvalds 
860346c7a68STakashi Iwai static void snd_opti93x_capture_format(struct snd_opti93x *chip, unsigned char fmt)
8611da177e4SLinus Torvalds {
8621da177e4SLinus Torvalds 	snd_opti93x_mute(chip, 1);
8631da177e4SLinus Torvalds 
8641da177e4SLinus Torvalds 	snd_opti93x_mce_up(chip);
8651da177e4SLinus Torvalds 	if (!(chip->mode & OPTi93X_MODE_PLAY))
8661da177e4SLinus Torvalds 		snd_opti93x_out_mask(chip, OPTi93X_PLAY_FORMAT, 0x0f, fmt);
8671da177e4SLinus Torvalds 	else
8681da177e4SLinus Torvalds 		fmt = chip->image[OPTi93X_PLAY_FORMAT] & 0xf0;
8691da177e4SLinus Torvalds 	snd_opti93x_out_image(chip, OPTi93X_CAPT_FORMAT, fmt);
8701da177e4SLinus Torvalds 	snd_opti93x_mce_down(chip);
8711da177e4SLinus Torvalds 
8721da177e4SLinus Torvalds 	snd_opti93x_mute(chip, 0);
8731da177e4SLinus Torvalds }
8741da177e4SLinus Torvalds 
8751da177e4SLinus Torvalds 
876346c7a68STakashi Iwai static int snd_opti93x_open(struct snd_opti93x *chip, unsigned int mode)
8771da177e4SLinus Torvalds {
8781da177e4SLinus Torvalds 	unsigned long flags;
8791da177e4SLinus Torvalds 
8801da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
8811da177e4SLinus Torvalds 
8821da177e4SLinus Torvalds 	if (chip->mode & mode) {
8831da177e4SLinus Torvalds 		spin_unlock_irqrestore(&chip->lock, flags);
8841da177e4SLinus Torvalds 		return -EAGAIN;
8851da177e4SLinus Torvalds 	}
8861da177e4SLinus Torvalds 
8871da177e4SLinus Torvalds 	if (!(chip->mode & OPTi93X_MODE_OPEN)) {
8881da177e4SLinus Torvalds 		outb(0x00, OPTi93X_PORT(chip, STATUS));
8891da177e4SLinus Torvalds 		snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL,
8901da177e4SLinus Torvalds 			OPTi93X_IRQ_ENABLE, OPTi93X_IRQ_ENABLE);
8911da177e4SLinus Torvalds 		chip->mode = mode;
8921da177e4SLinus Torvalds 	}
8931da177e4SLinus Torvalds 	else
8941da177e4SLinus Torvalds 		chip->mode |= mode;
8951da177e4SLinus Torvalds 
8961da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
8971da177e4SLinus Torvalds 	return 0;
8981da177e4SLinus Torvalds }
8991da177e4SLinus Torvalds 
900346c7a68STakashi Iwai static void snd_opti93x_close(struct snd_opti93x *chip, unsigned int mode)
9011da177e4SLinus Torvalds {
9021da177e4SLinus Torvalds 	unsigned long flags;
9031da177e4SLinus Torvalds 
9041da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
9051da177e4SLinus Torvalds 
9061da177e4SLinus Torvalds 	chip->mode &= ~mode;
9071da177e4SLinus Torvalds 	if (chip->mode & OPTi93X_MODE_OPEN) {
9081da177e4SLinus Torvalds 		spin_unlock_irqrestore(&chip->lock, flags);
9091da177e4SLinus Torvalds 		return;
9101da177e4SLinus Torvalds 	}
9111da177e4SLinus Torvalds 
9121da177e4SLinus Torvalds 	snd_opti93x_mute(chip, 1);
9131da177e4SLinus Torvalds 
9141da177e4SLinus Torvalds 	outb(0, OPTi93X_PORT(chip, STATUS));
9151da177e4SLinus Torvalds 	snd_opti93x_out_mask(chip, OPTi93X_PIN_CTRL, OPTi93X_IRQ_ENABLE,
9161da177e4SLinus Torvalds 		~OPTi93X_IRQ_ENABLE);
9171da177e4SLinus Torvalds 
9181da177e4SLinus Torvalds 	snd_opti93x_mce_up(chip);
9191da177e4SLinus Torvalds 	snd_opti93x_out_image(chip, OPTi93X_IFACE_CONF, 0x00);
9201da177e4SLinus Torvalds 	snd_opti93x_mce_down(chip);
9211da177e4SLinus Torvalds 	chip->mode = 0;
9221da177e4SLinus Torvalds 
9231da177e4SLinus Torvalds 	snd_opti93x_mute(chip, 0);
9241da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
9251da177e4SLinus Torvalds }
9261da177e4SLinus Torvalds 
927346c7a68STakashi Iwai static int snd_opti93x_trigger(struct snd_pcm_substream *substream,
9281da177e4SLinus Torvalds 			       unsigned char what, int cmd)
9291da177e4SLinus Torvalds {
930346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
9311da177e4SLinus Torvalds 
9321da177e4SLinus Torvalds 	switch (cmd) {
9331da177e4SLinus Torvalds 	case SNDRV_PCM_TRIGGER_START:
9341da177e4SLinus Torvalds 	case SNDRV_PCM_TRIGGER_STOP:
9351da177e4SLinus Torvalds 	{
9361da177e4SLinus Torvalds 		unsigned int what = 0;
9371da177e4SLinus Torvalds 		struct list_head *pos;
938346c7a68STakashi Iwai 		struct snd_pcm_substream *s;
9391da177e4SLinus Torvalds 		snd_pcm_group_for_each(pos, substream) {
9401da177e4SLinus Torvalds 			s = snd_pcm_group_substream_entry(pos);
9411da177e4SLinus Torvalds 			if (s == chip->playback_substream) {
9421da177e4SLinus Torvalds 				what |= OPTi93X_PLAYBACK_ENABLE;
9431da177e4SLinus Torvalds 				snd_pcm_trigger_done(s, substream);
9441da177e4SLinus Torvalds 			} else if (s == chip->capture_substream) {
9451da177e4SLinus Torvalds 				what |= OPTi93X_CAPTURE_ENABLE;
9461da177e4SLinus Torvalds 				snd_pcm_trigger_done(s, substream);
9471da177e4SLinus Torvalds 			}
9481da177e4SLinus Torvalds 		}
9491da177e4SLinus Torvalds 		spin_lock(&chip->lock);
9501da177e4SLinus Torvalds 		if (cmd == SNDRV_PCM_TRIGGER_START) {
9511da177e4SLinus Torvalds 			snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, what);
9521da177e4SLinus Torvalds 			if (what & OPTi93X_CAPTURE_ENABLE)
9531da177e4SLinus Torvalds 				udelay(50);
9541da177e4SLinus Torvalds 		} else
9551da177e4SLinus Torvalds 			snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF, what, 0x00);
9561da177e4SLinus Torvalds 		spin_unlock(&chip->lock);
9571da177e4SLinus Torvalds 		break;
9581da177e4SLinus Torvalds 	}
9591da177e4SLinus Torvalds 	default:
9601da177e4SLinus Torvalds 		return -EINVAL;
9611da177e4SLinus Torvalds 	}
9621da177e4SLinus Torvalds 	return 0;
9631da177e4SLinus Torvalds }
9641da177e4SLinus Torvalds 
965346c7a68STakashi Iwai static int snd_opti93x_playback_trigger(struct snd_pcm_substream *substream, int cmd)
9661da177e4SLinus Torvalds {
9671da177e4SLinus Torvalds 	return snd_opti93x_trigger(substream,
9681da177e4SLinus Torvalds 				   OPTi93X_PLAYBACK_ENABLE, cmd);
9691da177e4SLinus Torvalds }
9701da177e4SLinus Torvalds 
971346c7a68STakashi Iwai static int snd_opti93x_capture_trigger(struct snd_pcm_substream *substream, int cmd)
9721da177e4SLinus Torvalds {
9731da177e4SLinus Torvalds 	return snd_opti93x_trigger(substream,
9741da177e4SLinus Torvalds 				   OPTi93X_CAPTURE_ENABLE, cmd);
9751da177e4SLinus Torvalds }
9761da177e4SLinus Torvalds 
977346c7a68STakashi Iwai static int snd_opti93x_hw_params(struct snd_pcm_substream *substream,
978346c7a68STakashi Iwai 				 struct snd_pcm_hw_params *hw_params)
9791da177e4SLinus Torvalds {
9801da177e4SLinus Torvalds 	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
9811da177e4SLinus Torvalds }
9821da177e4SLinus Torvalds 
9831da177e4SLinus Torvalds 
984346c7a68STakashi Iwai static int snd_opti93x_hw_free(struct snd_pcm_substream *substream)
9851da177e4SLinus Torvalds {
9861da177e4SLinus Torvalds 	snd_pcm_lib_free_pages(substream);
9871da177e4SLinus Torvalds 	return 0;
9881da177e4SLinus Torvalds }
9891da177e4SLinus Torvalds 
9901da177e4SLinus Torvalds 
991346c7a68STakashi Iwai static int snd_opti93x_playback_prepare(struct snd_pcm_substream *substream)
9921da177e4SLinus Torvalds {
993346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
994346c7a68STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
9951da177e4SLinus Torvalds 	unsigned long flags;
9961da177e4SLinus Torvalds 	unsigned char format;
9971da177e4SLinus Torvalds 	unsigned int count = snd_pcm_lib_period_bytes(substream);
9981da177e4SLinus Torvalds 	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
9991da177e4SLinus Torvalds 
10001da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
10011da177e4SLinus Torvalds 
10021da177e4SLinus Torvalds 	chip->p_dma_size = size;
10031da177e4SLinus Torvalds 	snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF,
10041da177e4SLinus Torvalds 		OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO,
10051da177e4SLinus Torvalds 		~(OPTi93X_PLAYBACK_ENABLE | OPTi93X_PLAYBACK_PIO));
10061da177e4SLinus Torvalds 
10071da177e4SLinus Torvalds 	snd_dma_program(chip->dma1, runtime->dma_addr, size,
10081da177e4SLinus Torvalds 		DMA_MODE_WRITE | DMA_AUTOINIT);
10091da177e4SLinus Torvalds 
10101da177e4SLinus Torvalds 	format = snd_opti93x_get_freq(runtime->rate);
10111da177e4SLinus Torvalds 	format |= snd_opti93x_get_format(chip, runtime->format,
10121da177e4SLinus Torvalds 		runtime->channels);
10131da177e4SLinus Torvalds 	snd_opti93x_playback_format(chip, format);
10141da177e4SLinus Torvalds 	format = chip->image[OPTi93X_PLAY_FORMAT];
10151da177e4SLinus Torvalds 
10161da177e4SLinus Torvalds 	count = snd_opti93x_get_count(format, count) - 1;
10171da177e4SLinus Torvalds 	snd_opti93x_out_image(chip, OPTi93X_PLAY_LWR_CNT, count);
10181da177e4SLinus Torvalds 	snd_opti93x_out_image(chip, OPTi93X_PLAY_UPR_CNT, count >> 8);
10191da177e4SLinus Torvalds 
10201da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
10211da177e4SLinus Torvalds 	return 0;
10221da177e4SLinus Torvalds }
10231da177e4SLinus Torvalds 
1024346c7a68STakashi Iwai static int snd_opti93x_capture_prepare(struct snd_pcm_substream *substream)
10251da177e4SLinus Torvalds {
1026346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
1027346c7a68STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
10281da177e4SLinus Torvalds 	unsigned long flags;
10291da177e4SLinus Torvalds 	unsigned char format;
10301da177e4SLinus Torvalds 	unsigned int count = snd_pcm_lib_period_bytes(substream);
10311da177e4SLinus Torvalds 	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
10321da177e4SLinus Torvalds 
10331da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
10341da177e4SLinus Torvalds 
10351da177e4SLinus Torvalds 	chip->c_dma_size = size;
10361da177e4SLinus Torvalds 	snd_opti93x_out_mask(chip, OPTi93X_IFACE_CONF,
1037db67319aSClemens Ladisch 		OPTi93X_CAPTURE_ENABLE | OPTi93X_CAPTURE_PIO, 0);
10381da177e4SLinus Torvalds 
10391da177e4SLinus Torvalds 	snd_dma_program(chip->dma2, runtime->dma_addr, size,
10401da177e4SLinus Torvalds 		DMA_MODE_READ | DMA_AUTOINIT);
10411da177e4SLinus Torvalds 
10421da177e4SLinus Torvalds 	format = snd_opti93x_get_freq(runtime->rate);
10431da177e4SLinus Torvalds 	format |= snd_opti93x_get_format(chip, runtime->format,
10441da177e4SLinus Torvalds 		runtime->channels);
10451da177e4SLinus Torvalds 	snd_opti93x_capture_format(chip, format);
10461da177e4SLinus Torvalds 	format = chip->image[OPTi93X_CAPT_FORMAT];
10471da177e4SLinus Torvalds 
10481da177e4SLinus Torvalds 	count = snd_opti93x_get_count(format, count) - 1;
10491da177e4SLinus Torvalds 	snd_opti93x_out_image(chip, OPTi93X_CAPT_LWR_CNT, count);
10501da177e4SLinus Torvalds 	snd_opti93x_out_image(chip, OPTi93X_CAPT_UPR_CNT, count >> 8);
10511da177e4SLinus Torvalds 
10521da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
10531da177e4SLinus Torvalds 	return 0;
10541da177e4SLinus Torvalds }
10551da177e4SLinus Torvalds 
1056346c7a68STakashi Iwai static snd_pcm_uframes_t snd_opti93x_playback_pointer(struct snd_pcm_substream *substream)
10571da177e4SLinus Torvalds {
1058346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
10591da177e4SLinus Torvalds 	size_t ptr;
10601da177e4SLinus Torvalds 
10611da177e4SLinus Torvalds 	if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_PLAYBACK_ENABLE))
10621da177e4SLinus Torvalds 		return 0;
10631da177e4SLinus Torvalds 
10641da177e4SLinus Torvalds 	ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size);
10651da177e4SLinus Torvalds 	return bytes_to_frames(substream->runtime, ptr);
10661da177e4SLinus Torvalds }
10671da177e4SLinus Torvalds 
1068346c7a68STakashi Iwai static snd_pcm_uframes_t snd_opti93x_capture_pointer(struct snd_pcm_substream *substream)
10691da177e4SLinus Torvalds {
1070346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
10711da177e4SLinus Torvalds 	size_t ptr;
10721da177e4SLinus Torvalds 
10731da177e4SLinus Torvalds 	if (!(chip->image[OPTi93X_IFACE_CONF] & OPTi93X_CAPTURE_ENABLE))
10741da177e4SLinus Torvalds 		return 0;
10751da177e4SLinus Torvalds 
10761da177e4SLinus Torvalds 	ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size);
10771da177e4SLinus Torvalds 	return bytes_to_frames(substream->runtime, ptr);
10781da177e4SLinus Torvalds }
10791da177e4SLinus Torvalds 
10801da177e4SLinus Torvalds 
1081346c7a68STakashi Iwai static void snd_opti93x_overrange(struct snd_opti93x *chip)
10821da177e4SLinus Torvalds {
10831da177e4SLinus Torvalds 	unsigned long flags;
10841da177e4SLinus Torvalds 
10851da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
10861da177e4SLinus Torvalds 
10871da177e4SLinus Torvalds 	if (snd_opti93x_in(chip, OPTi93X_ERR_INIT) & (0x08 | 0x02))
10881da177e4SLinus Torvalds 		chip->capture_substream->runtime->overrange++;
10891da177e4SLinus Torvalds 
10901da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
10911da177e4SLinus Torvalds }
10921da177e4SLinus Torvalds 
10931da177e4SLinus Torvalds static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id, struct pt_regs *regs)
10941da177e4SLinus Torvalds {
1095346c7a68STakashi Iwai 	struct snd_opti93x *codec = dev_id;
10961da177e4SLinus Torvalds 	unsigned char status;
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds 	status = snd_opti9xx_read(codec->chip, OPTi9XX_MC_REG(11));
10991da177e4SLinus Torvalds 	if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)
11001da177e4SLinus Torvalds 		snd_pcm_period_elapsed(codec->playback_substream);
11011da177e4SLinus Torvalds 	if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) {
11021da177e4SLinus Torvalds 		snd_opti93x_overrange(codec);
11031da177e4SLinus Torvalds 		snd_pcm_period_elapsed(codec->capture_substream);
11041da177e4SLinus Torvalds 	}
11051da177e4SLinus Torvalds 	outb(0x00, OPTi93X_PORT(codec, STATUS));
11061da177e4SLinus Torvalds 	return IRQ_HANDLED;
11071da177e4SLinus Torvalds }
11081da177e4SLinus Torvalds 
11091da177e4SLinus Torvalds 
1110346c7a68STakashi Iwai static struct snd_pcm_hardware snd_opti93x_playback = {
11111da177e4SLinus Torvalds 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
11121da177e4SLinus Torvalds 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
11131da177e4SLinus Torvalds 	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
11141da177e4SLinus Torvalds 				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
11151da177e4SLinus Torvalds 	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
11161da177e4SLinus Torvalds 	.rate_min =		5512,
11171da177e4SLinus Torvalds 	.rate_max =		48000,
11181da177e4SLinus Torvalds 	.channels_min =		1,
11191da177e4SLinus Torvalds 	.channels_max =		2,
11201da177e4SLinus Torvalds 	.buffer_bytes_max =	(128*1024),
11211da177e4SLinus Torvalds 	.period_bytes_min =	64,
11221da177e4SLinus Torvalds 	.period_bytes_max =	(128*1024),
11231da177e4SLinus Torvalds 	.periods_min =		1,
11241da177e4SLinus Torvalds 	.periods_max =		1024,
11251da177e4SLinus Torvalds 	.fifo_size =		0,
11261da177e4SLinus Torvalds };
11271da177e4SLinus Torvalds 
1128346c7a68STakashi Iwai static struct snd_pcm_hardware snd_opti93x_capture = {
11291da177e4SLinus Torvalds 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
11301da177e4SLinus Torvalds 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
11311da177e4SLinus Torvalds 	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
11321da177e4SLinus Torvalds 				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
11331da177e4SLinus Torvalds 	.rates =		SNDRV_PCM_RATE_8000_48000,
11341da177e4SLinus Torvalds 	.rate_min =		5512,
11351da177e4SLinus Torvalds 	.rate_max =		48000,
11361da177e4SLinus Torvalds 	.channels_min =		1,
11371da177e4SLinus Torvalds 	.channels_max =		2,
11381da177e4SLinus Torvalds 	.buffer_bytes_max =	(128*1024),
11391da177e4SLinus Torvalds 	.period_bytes_min =	64,
11401da177e4SLinus Torvalds 	.period_bytes_max =	(128*1024),
11411da177e4SLinus Torvalds 	.periods_min =		1,
11421da177e4SLinus Torvalds 	.periods_max =		1024,
11431da177e4SLinus Torvalds 	.fifo_size =		0,
11441da177e4SLinus Torvalds };
11451da177e4SLinus Torvalds 
1146346c7a68STakashi Iwai static int snd_opti93x_playback_open(struct snd_pcm_substream *substream)
11471da177e4SLinus Torvalds {
11481da177e4SLinus Torvalds 	int error;
1149346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
1150346c7a68STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
11511da177e4SLinus Torvalds 
11521da177e4SLinus Torvalds 	if ((error = snd_opti93x_open(chip, OPTi93X_MODE_PLAY)) < 0)
11531da177e4SLinus Torvalds 		return error;
11541da177e4SLinus Torvalds 	snd_pcm_set_sync(substream);
11551da177e4SLinus Torvalds 	chip->playback_substream = substream;
11561da177e4SLinus Torvalds 	runtime->hw = snd_opti93x_playback;
11571da177e4SLinus Torvalds 	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
11581da177e4SLinus Torvalds 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
11591da177e4SLinus Torvalds 	return error;
11601da177e4SLinus Torvalds }
11611da177e4SLinus Torvalds 
1162346c7a68STakashi Iwai static int snd_opti93x_capture_open(struct snd_pcm_substream *substream)
11631da177e4SLinus Torvalds {
11641da177e4SLinus Torvalds 	int error;
1165346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
1166346c7a68STakashi Iwai 	struct snd_pcm_runtime *runtime = substream->runtime;
11671da177e4SLinus Torvalds 
11681da177e4SLinus Torvalds 	if ((error = snd_opti93x_open(chip, OPTi93X_MODE_CAPTURE)) < 0)
11691da177e4SLinus Torvalds 		return error;
11701da177e4SLinus Torvalds 	runtime->hw = snd_opti93x_capture;
11711da177e4SLinus Torvalds 	snd_pcm_set_sync(substream);
11721da177e4SLinus Torvalds 	chip->capture_substream = substream;
11731da177e4SLinus Torvalds 	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
11741da177e4SLinus Torvalds 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
11751da177e4SLinus Torvalds 	return error;
11761da177e4SLinus Torvalds }
11771da177e4SLinus Torvalds 
1178346c7a68STakashi Iwai static int snd_opti93x_playback_close(struct snd_pcm_substream *substream)
11791da177e4SLinus Torvalds {
1180346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
11811da177e4SLinus Torvalds 
11821da177e4SLinus Torvalds 	chip->playback_substream = NULL;
11831da177e4SLinus Torvalds 	snd_opti93x_close(chip, OPTi93X_MODE_PLAY);
11841da177e4SLinus Torvalds 	return 0;
11851da177e4SLinus Torvalds }
11861da177e4SLinus Torvalds 
1187346c7a68STakashi Iwai static int snd_opti93x_capture_close(struct snd_pcm_substream *substream)
11881da177e4SLinus Torvalds {
1189346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_pcm_substream_chip(substream);
11901da177e4SLinus Torvalds 
11911da177e4SLinus Torvalds 	chip->capture_substream = NULL;
11921da177e4SLinus Torvalds 	snd_opti93x_close(chip, OPTi93X_MODE_CAPTURE);
11931da177e4SLinus Torvalds 	return 0;
11941da177e4SLinus Torvalds }
11951da177e4SLinus Torvalds 
11961da177e4SLinus Torvalds 
1197346c7a68STakashi Iwai static void snd_opti93x_init(struct snd_opti93x *chip)
11981da177e4SLinus Torvalds {
11991da177e4SLinus Torvalds 	unsigned long flags;
12001da177e4SLinus Torvalds 	int i;
12011da177e4SLinus Torvalds 
12021da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
12031da177e4SLinus Torvalds 	snd_opti93x_mce_up(chip);
12041da177e4SLinus Torvalds 
12051da177e4SLinus Torvalds 	for (i = 0; i < 32; i++)
12061da177e4SLinus Torvalds 		snd_opti93x_out_image(chip, i, snd_opti93x_default_image[i]);
12071da177e4SLinus Torvalds 
12081da177e4SLinus Torvalds 	snd_opti93x_mce_down(chip);
12091da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
12101da177e4SLinus Torvalds }
12111da177e4SLinus Torvalds 
1212346c7a68STakashi Iwai static int snd_opti93x_probe(struct snd_opti93x *chip)
12131da177e4SLinus Torvalds {
12141da177e4SLinus Torvalds 	unsigned long flags;
12151da177e4SLinus Torvalds 	unsigned char val;
12161da177e4SLinus Torvalds 
12171da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
12181da177e4SLinus Torvalds 	val = snd_opti93x_in(chip, OPTi93X_ID) & 0x0f;
12191da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
12201da177e4SLinus Torvalds 
12211da177e4SLinus Torvalds 	return (val == 0x0a) ? 0 : -ENODEV;
12221da177e4SLinus Torvalds }
12231da177e4SLinus Torvalds 
1224346c7a68STakashi Iwai static int snd_opti93x_free(struct snd_opti93x *chip)
12251da177e4SLinus Torvalds {
1226b1d5776dSTakashi Iwai 	release_and_free_resource(chip->res_port);
12271da177e4SLinus Torvalds 	if (chip->dma1 >= 0) {
12281da177e4SLinus Torvalds 		disable_dma(chip->dma1);
12291da177e4SLinus Torvalds 		free_dma(chip->dma1);
12301da177e4SLinus Torvalds 	}
12311da177e4SLinus Torvalds 	if (chip->dma2 >= 0) {
12321da177e4SLinus Torvalds 		disable_dma(chip->dma2);
12331da177e4SLinus Torvalds 		free_dma(chip->dma2);
12341da177e4SLinus Torvalds 	}
12351da177e4SLinus Torvalds 	if (chip->irq >= 0) {
12361da177e4SLinus Torvalds 	  free_irq(chip->irq, chip);
12371da177e4SLinus Torvalds 	}
12381da177e4SLinus Torvalds 	kfree(chip);
12391da177e4SLinus Torvalds 	return 0;
12401da177e4SLinus Torvalds }
12411da177e4SLinus Torvalds 
1242346c7a68STakashi Iwai static int snd_opti93x_dev_free(struct snd_device *device)
12431da177e4SLinus Torvalds {
1244346c7a68STakashi Iwai 	struct snd_opti93x *chip = device->device_data;
12451da177e4SLinus Torvalds 	return snd_opti93x_free(chip);
12461da177e4SLinus Torvalds }
12471da177e4SLinus Torvalds 
1248346c7a68STakashi Iwai static const char *snd_opti93x_chip_id(struct snd_opti93x *codec)
12491da177e4SLinus Torvalds {
12501da177e4SLinus Torvalds 	switch (codec->hardware) {
12511da177e4SLinus Torvalds 	case OPTi9XX_HW_82C930: return "82C930";
12521da177e4SLinus Torvalds 	case OPTi9XX_HW_82C931: return "82C931";
12531da177e4SLinus Torvalds 	case OPTi9XX_HW_82C933: return "82C933";
12541da177e4SLinus Torvalds 	default:		return "???";
12551da177e4SLinus Torvalds 	}
12561da177e4SLinus Torvalds }
12571da177e4SLinus Torvalds 
1258346c7a68STakashi Iwai static int snd_opti93x_create(struct snd_card *card, struct snd_opti9xx *chip,
12591da177e4SLinus Torvalds 			      int dma1, int dma2,
1260346c7a68STakashi Iwai 			      struct snd_opti93x **rcodec)
12611da177e4SLinus Torvalds {
1262346c7a68STakashi Iwai 	static struct snd_device_ops ops = {
12631da177e4SLinus Torvalds 		.dev_free =	snd_opti93x_dev_free,
12641da177e4SLinus Torvalds 	};
12651da177e4SLinus Torvalds 	int error;
1266346c7a68STakashi Iwai 	struct snd_opti93x *codec;
12671da177e4SLinus Torvalds 
12681da177e4SLinus Torvalds 	*rcodec = NULL;
12699e76a76eSTakashi Iwai 	codec = kzalloc(sizeof(*codec), GFP_KERNEL);
12701da177e4SLinus Torvalds 	if (codec == NULL)
12711da177e4SLinus Torvalds 		return -ENOMEM;
12721da177e4SLinus Torvalds 	codec->irq = -1;
12731da177e4SLinus Torvalds 	codec->dma1 = -1;
12741da177e4SLinus Torvalds 	codec->dma2 = -1;
12751da177e4SLinus Torvalds 
12761da177e4SLinus Torvalds 	if ((codec->res_port = request_region(chip->wss_base + 4, 4, "OPTI93x CODEC")) == NULL) {
12771da177e4SLinus Torvalds 		snd_printk(KERN_ERR "opti9xx: can't grab port 0x%lx\n", chip->wss_base + 4);
12781da177e4SLinus Torvalds 		snd_opti93x_free(codec);
12791da177e4SLinus Torvalds 		return -EBUSY;
12801da177e4SLinus Torvalds 	}
12811da177e4SLinus Torvalds 	if (request_dma(dma1, "OPTI93x - 1")) {
12821da177e4SLinus Torvalds 		snd_printk(KERN_ERR "opti9xx: can't grab DMA1 %d\n", dma1);
12831da177e4SLinus Torvalds 		snd_opti93x_free(codec);
12841da177e4SLinus Torvalds 		return -EBUSY;
12851da177e4SLinus Torvalds 	}
12861da177e4SLinus Torvalds 	codec->dma1 = chip->dma1;
12871da177e4SLinus Torvalds 	if (request_dma(dma2, "OPTI93x - 2")) {
12881da177e4SLinus Torvalds 		snd_printk(KERN_ERR "opti9xx: can't grab DMA2 %d\n", dma2);
12891da177e4SLinus Torvalds 		snd_opti93x_free(codec);
12901da177e4SLinus Torvalds 		return -EBUSY;
12911da177e4SLinus Torvalds 	}
12921da177e4SLinus Torvalds 	codec->dma2 = chip->dma2;
12931da177e4SLinus Torvalds 
1294*65ca68b3SThomas Gleixner 	if (request_irq(chip->irq, snd_opti93x_interrupt, IRQF_DISABLED, DRIVER_NAME" - WSS", codec)) {
12951da177e4SLinus Torvalds 		snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq);
12961da177e4SLinus Torvalds 		snd_opti93x_free(codec);
12971da177e4SLinus Torvalds 		return -EBUSY;
12981da177e4SLinus Torvalds 	}
12991da177e4SLinus Torvalds 
13001da177e4SLinus Torvalds 	codec->card = card;
13011da177e4SLinus Torvalds 	codec->port = chip->wss_base + 4;
13021da177e4SLinus Torvalds 	codec->irq = chip->irq;
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds 	spin_lock_init(&codec->lock);
13051da177e4SLinus Torvalds 	codec->hardware = chip->hardware;
13061da177e4SLinus Torvalds 	codec->chip = chip;
13071da177e4SLinus Torvalds 
13081da177e4SLinus Torvalds 	if ((error = snd_opti93x_probe(codec))) {
13091da177e4SLinus Torvalds 		snd_opti93x_free(codec);
13101da177e4SLinus Torvalds 		return error;
13111da177e4SLinus Torvalds 	}
13121da177e4SLinus Torvalds 
13131da177e4SLinus Torvalds 	snd_opti93x_init(codec);
13141da177e4SLinus Torvalds 
13151da177e4SLinus Torvalds 	/* Register device */
13161da177e4SLinus Torvalds 	if ((error = snd_device_new(card, SNDRV_DEV_LOWLEVEL, codec, &ops)) < 0) {
13171da177e4SLinus Torvalds 		snd_opti93x_free(codec);
13181da177e4SLinus Torvalds 		return error;
13191da177e4SLinus Torvalds 	}
13201da177e4SLinus Torvalds 
13211da177e4SLinus Torvalds 	*rcodec = codec;
13221da177e4SLinus Torvalds 	return 0;
13231da177e4SLinus Torvalds }
13241da177e4SLinus Torvalds 
1325346c7a68STakashi Iwai static struct snd_pcm_ops snd_opti93x_playback_ops = {
13261da177e4SLinus Torvalds 	.open =		snd_opti93x_playback_open,
13271da177e4SLinus Torvalds 	.close =	snd_opti93x_playback_close,
13281da177e4SLinus Torvalds 	.ioctl =	snd_pcm_lib_ioctl,
13291da177e4SLinus Torvalds 	.hw_params =	snd_opti93x_hw_params,
13301da177e4SLinus Torvalds 	.hw_free =	snd_opti93x_hw_free,
13311da177e4SLinus Torvalds 	.prepare =	snd_opti93x_playback_prepare,
13321da177e4SLinus Torvalds 	.trigger =	snd_opti93x_playback_trigger,
13331da177e4SLinus Torvalds 	.pointer =	snd_opti93x_playback_pointer,
13341da177e4SLinus Torvalds };
13351da177e4SLinus Torvalds 
1336346c7a68STakashi Iwai static struct snd_pcm_ops snd_opti93x_capture_ops = {
13371da177e4SLinus Torvalds 	.open =		snd_opti93x_capture_open,
13381da177e4SLinus Torvalds 	.close =	snd_opti93x_capture_close,
13391da177e4SLinus Torvalds 	.ioctl =	snd_pcm_lib_ioctl,
13401da177e4SLinus Torvalds 	.hw_params =	snd_opti93x_hw_params,
13411da177e4SLinus Torvalds 	.hw_free =	snd_opti93x_hw_free,
13421da177e4SLinus Torvalds 	.prepare =	snd_opti93x_capture_prepare,
13431da177e4SLinus Torvalds 	.trigger =	snd_opti93x_capture_trigger,
13441da177e4SLinus Torvalds 	.pointer =	snd_opti93x_capture_pointer,
13451da177e4SLinus Torvalds };
13461da177e4SLinus Torvalds 
1347346c7a68STakashi Iwai static int snd_opti93x_pcm(struct snd_opti93x *codec, int device, struct snd_pcm **rpcm)
13481da177e4SLinus Torvalds {
13491da177e4SLinus Torvalds 	int error;
1350346c7a68STakashi Iwai 	struct snd_pcm *pcm;
13511da177e4SLinus Torvalds 
135291134859STakashi Iwai 	if ((error = snd_pcm_new(codec->card, "OPTi 82C93X", device, 1, 1, &pcm)) < 0)
13531da177e4SLinus Torvalds 		return error;
13541da177e4SLinus Torvalds 
13551da177e4SLinus Torvalds 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_opti93x_playback_ops);
13561da177e4SLinus Torvalds 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_opti93x_capture_ops);
13571da177e4SLinus Torvalds 
13581da177e4SLinus Torvalds 	pcm->private_data = codec;
13591da177e4SLinus Torvalds 	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
13601da177e4SLinus Torvalds 
13611da177e4SLinus Torvalds 	strcpy(pcm->name, snd_opti93x_chip_id(codec));
13621da177e4SLinus Torvalds 
13631da177e4SLinus Torvalds 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
13641da177e4SLinus Torvalds 					      snd_dma_isa_data(),
13651da177e4SLinus Torvalds 					      64*1024, codec->dma1 > 3 || codec->dma2 > 3 ? 128*1024 : 64*1024);
13661da177e4SLinus Torvalds 
13671da177e4SLinus Torvalds 	codec->pcm = pcm;
13681da177e4SLinus Torvalds 	if (rpcm)
13691da177e4SLinus Torvalds 		*rpcm = pcm;
13701da177e4SLinus Torvalds 	return 0;
13711da177e4SLinus Torvalds }
13721da177e4SLinus Torvalds 
13731da177e4SLinus Torvalds /*
13741da177e4SLinus Torvalds  *  MIXER part
13751da177e4SLinus Torvalds  */
13761da177e4SLinus Torvalds 
1377346c7a68STakashi Iwai static int snd_opti93x_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
13781da177e4SLinus Torvalds {
13791da177e4SLinus Torvalds 	static char *texts[4] = {
13801da177e4SLinus Torvalds 		"Line1", "Aux", "Mic", "Mix"
13811da177e4SLinus Torvalds 	};
13821da177e4SLinus Torvalds 
13831da177e4SLinus Torvalds 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
13841da177e4SLinus Torvalds 	uinfo->count = 2;
13851da177e4SLinus Torvalds 	uinfo->value.enumerated.items = 4;
13861da177e4SLinus Torvalds 	if (uinfo->value.enumerated.item > 3)
13871da177e4SLinus Torvalds 		uinfo->value.enumerated.item = 3;
13881da177e4SLinus Torvalds 	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
13891da177e4SLinus Torvalds 	return 0;
13901da177e4SLinus Torvalds }
13911da177e4SLinus Torvalds 
1392346c7a68STakashi Iwai static int snd_opti93x_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
13931da177e4SLinus Torvalds {
1394346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
13951da177e4SLinus Torvalds 	unsigned long flags;
13961da177e4SLinus Torvalds 
13971da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
13981da177e4SLinus Torvalds 	ucontrol->value.enumerated.item[0] = (chip->image[OPTi93X_MIXOUT_LEFT] & OPTi93X_MIXOUT_MIXER) >> 6;
13991da177e4SLinus Torvalds 	ucontrol->value.enumerated.item[1] = (chip->image[OPTi93X_MIXOUT_RIGHT] & OPTi93X_MIXOUT_MIXER) >> 6;
14001da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
14011da177e4SLinus Torvalds 	return 0;
14021da177e4SLinus Torvalds }
14031da177e4SLinus Torvalds 
1404346c7a68STakashi Iwai static int snd_opti93x_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
14051da177e4SLinus Torvalds {
1406346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
14071da177e4SLinus Torvalds 	unsigned long flags;
14081da177e4SLinus Torvalds 	unsigned short left, right;
14091da177e4SLinus Torvalds 	int change;
14101da177e4SLinus Torvalds 
14111da177e4SLinus Torvalds 	if (ucontrol->value.enumerated.item[0] > 3 ||
14121da177e4SLinus Torvalds 	    ucontrol->value.enumerated.item[1] > 3)
14131da177e4SLinus Torvalds 		return -EINVAL;
14141da177e4SLinus Torvalds 	left = ucontrol->value.enumerated.item[0] << 6;
14151da177e4SLinus Torvalds 	right = ucontrol->value.enumerated.item[1] << 6;
14161da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
14171da177e4SLinus Torvalds 	left = (chip->image[OPTi93X_MIXOUT_LEFT] & ~OPTi93X_MIXOUT_MIXER) | left;
14181da177e4SLinus Torvalds 	right = (chip->image[OPTi93X_MIXOUT_RIGHT] & ~OPTi93X_MIXOUT_MIXER) | right;
14191da177e4SLinus Torvalds 	change = left != chip->image[OPTi93X_MIXOUT_LEFT] ||
14201da177e4SLinus Torvalds 	         right != chip->image[OPTi93X_MIXOUT_RIGHT];
14211da177e4SLinus Torvalds 	snd_opti93x_out_image(chip, OPTi93X_MIXOUT_LEFT, left);
14221da177e4SLinus Torvalds 	snd_opti93x_out_image(chip, OPTi93X_MIXOUT_RIGHT, right);
14231da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
14241da177e4SLinus Torvalds 	return change;
14251da177e4SLinus Torvalds }
14261da177e4SLinus Torvalds 
14271da177e4SLinus Torvalds #if 0
14281da177e4SLinus Torvalds 
14291da177e4SLinus Torvalds #define OPTi93X_SINGLE(xname, xindex, reg, shift, mask, invert) \
14301da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
14311da177e4SLinus Torvalds   .info = snd_opti93x_info_single, \
14321da177e4SLinus Torvalds   .get = snd_opti93x_get_single, .put = snd_opti93x_put_single, \
14331da177e4SLinus Torvalds   .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
14341da177e4SLinus Torvalds 
1435346c7a68STakashi Iwai static int snd_opti93x_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
14361da177e4SLinus Torvalds {
14371da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 16) & 0xff;
14381da177e4SLinus Torvalds 
14391da177e4SLinus Torvalds 	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
14401da177e4SLinus Torvalds 	uinfo->count = 1;
14411da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
14421da177e4SLinus Torvalds 	uinfo->value.integer.max = mask;
14431da177e4SLinus Torvalds 	return 0;
14441da177e4SLinus Torvalds }
14451da177e4SLinus Torvalds 
1446346c7a68STakashi Iwai static int snd_opti93x_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
14471da177e4SLinus Torvalds {
1448346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
14491da177e4SLinus Torvalds 	unsigned long flags;
14501da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
14511da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 8) & 0xff;
14521da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 16) & 0xff;
14531da177e4SLinus Torvalds 	int invert = (kcontrol->private_value >> 24) & 0xff;
14541da177e4SLinus Torvalds 
14551da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
14561da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
14571da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
14581da177e4SLinus Torvalds 	if (invert)
14591da177e4SLinus Torvalds 		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
14601da177e4SLinus Torvalds 	return 0;
14611da177e4SLinus Torvalds }
14621da177e4SLinus Torvalds 
1463346c7a68STakashi Iwai static int snd_opti93x_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
14641da177e4SLinus Torvalds {
1465346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
14661da177e4SLinus Torvalds 	unsigned long flags;
14671da177e4SLinus Torvalds 	int reg = kcontrol->private_value & 0xff;
14681da177e4SLinus Torvalds 	int shift = (kcontrol->private_value >> 8) & 0xff;
14691da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 16) & 0xff;
14701da177e4SLinus Torvalds 	int invert = (kcontrol->private_value >> 24) & 0xff;
14711da177e4SLinus Torvalds 	int change;
14721da177e4SLinus Torvalds 	unsigned short val;
14731da177e4SLinus Torvalds 
14741da177e4SLinus Torvalds 	val = (ucontrol->value.integer.value[0] & mask);
14751da177e4SLinus Torvalds 	if (invert)
14761da177e4SLinus Torvalds 		val = mask - val;
14771da177e4SLinus Torvalds 	val <<= shift;
14781da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
14791da177e4SLinus Torvalds 	val = (chip->image[reg] & ~(mask << shift)) | val;
14801da177e4SLinus Torvalds 	change = val != chip->image[reg];
14811da177e4SLinus Torvalds 	snd_opti93x_out(chip, reg, val);
14821da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
14831da177e4SLinus Torvalds 	return change;
14841da177e4SLinus Torvalds }
14851da177e4SLinus Torvalds 
14861da177e4SLinus Torvalds #endif /* single */
14871da177e4SLinus Torvalds 
14881da177e4SLinus Torvalds #define OPTi93X_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
14891da177e4SLinus Torvalds { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
14901da177e4SLinus Torvalds   .info = snd_opti93x_info_double, \
14911da177e4SLinus Torvalds   .get = snd_opti93x_get_double, .put = snd_opti93x_put_double, \
14921da177e4SLinus Torvalds   .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
14931da177e4SLinus Torvalds 
14941da177e4SLinus Torvalds #define OPTi93X_DOUBLE_INVERT_INVERT(xctl) \
14951da177e4SLinus Torvalds 	do { xctl.private_value ^= 22; } while (0)
14961da177e4SLinus Torvalds #define OPTi93X_DOUBLE_CHANGE_REGS(xctl, left_reg, right_reg) \
14971da177e4SLinus Torvalds 	do { xctl.private_value &= ~0x0000ffff; \
14981da177e4SLinus Torvalds 	     xctl.private_value |= left_reg | (right_reg << 8); } while (0)
14991da177e4SLinus Torvalds 
1500346c7a68STakashi Iwai static int snd_opti93x_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
15011da177e4SLinus Torvalds {
15021da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
15031da177e4SLinus Torvalds 
15041da177e4SLinus Torvalds 	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
15051da177e4SLinus Torvalds 	uinfo->count = 2;
15061da177e4SLinus Torvalds 	uinfo->value.integer.min = 0;
15071da177e4SLinus Torvalds 	uinfo->value.integer.max = mask;
15081da177e4SLinus Torvalds 	return 0;
15091da177e4SLinus Torvalds }
15101da177e4SLinus Torvalds 
1511346c7a68STakashi Iwai static int snd_opti93x_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
15121da177e4SLinus Torvalds {
1513346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
15141da177e4SLinus Torvalds 	unsigned long flags;
15151da177e4SLinus Torvalds 	int left_reg = kcontrol->private_value & 0xff;
15161da177e4SLinus Torvalds 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
15171da177e4SLinus Torvalds 	int shift_left = (kcontrol->private_value >> 16) & 0x07;
15181da177e4SLinus Torvalds 	int shift_right = (kcontrol->private_value >> 19) & 0x07;
15191da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
15201da177e4SLinus Torvalds 	int invert = (kcontrol->private_value >> 22) & 1;
15211da177e4SLinus Torvalds 
15221da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
15231da177e4SLinus Torvalds 	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
15241da177e4SLinus Torvalds 	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
15251da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
15261da177e4SLinus Torvalds 	if (invert) {
15271da177e4SLinus Torvalds 		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
15281da177e4SLinus Torvalds 		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
15291da177e4SLinus Torvalds 	}
15301da177e4SLinus Torvalds 	return 0;
15311da177e4SLinus Torvalds }
15321da177e4SLinus Torvalds 
1533346c7a68STakashi Iwai static int snd_opti93x_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
15341da177e4SLinus Torvalds {
1535346c7a68STakashi Iwai 	struct snd_opti93x *chip = snd_kcontrol_chip(kcontrol);
15361da177e4SLinus Torvalds 	unsigned long flags;
15371da177e4SLinus Torvalds 	int left_reg = kcontrol->private_value & 0xff;
15381da177e4SLinus Torvalds 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
15391da177e4SLinus Torvalds 	int shift_left = (kcontrol->private_value >> 16) & 0x07;
15401da177e4SLinus Torvalds 	int shift_right = (kcontrol->private_value >> 19) & 0x07;
15411da177e4SLinus Torvalds 	int mask = (kcontrol->private_value >> 24) & 0xff;
15421da177e4SLinus Torvalds 	int invert = (kcontrol->private_value >> 22) & 1;
15431da177e4SLinus Torvalds 	int change;
15441da177e4SLinus Torvalds 	unsigned short val1, val2;
15451da177e4SLinus Torvalds 
15461da177e4SLinus Torvalds 	val1 = ucontrol->value.integer.value[0] & mask;
15471da177e4SLinus Torvalds 	val2 = ucontrol->value.integer.value[1] & mask;
15481da177e4SLinus Torvalds 	if (invert) {
15491da177e4SLinus Torvalds 		val1 = mask - val1;
15501da177e4SLinus Torvalds 		val2 = mask - val2;
15511da177e4SLinus Torvalds 	}
15521da177e4SLinus Torvalds 	val1 <<= shift_left;
15531da177e4SLinus Torvalds 	val2 <<= shift_right;
15541da177e4SLinus Torvalds 	spin_lock_irqsave(&chip->lock, flags);
15551da177e4SLinus Torvalds 	val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
15561da177e4SLinus Torvalds 	val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
15571da177e4SLinus Torvalds 	change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg];
15581da177e4SLinus Torvalds 	snd_opti93x_out_image(chip, left_reg, val1);
15591da177e4SLinus Torvalds 	snd_opti93x_out_image(chip, right_reg, val2);
15601da177e4SLinus Torvalds 	spin_unlock_irqrestore(&chip->lock, flags);
15611da177e4SLinus Torvalds 	return change;
15621da177e4SLinus Torvalds }
15631da177e4SLinus Torvalds 
1564346c7a68STakashi Iwai static struct snd_kcontrol_new snd_opti93x_controls[] = {
15651da177e4SLinus Torvalds OPTi93X_DOUBLE("Master Playback Switch", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
15661da177e4SLinus Torvalds OPTi93X_DOUBLE("Master Playback Volume", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1),
15671da177e4SLinus Torvalds OPTi93X_DOUBLE("PCM Playback Switch", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 7, 7, 1, 1),
15681da177e4SLinus Torvalds OPTi93X_DOUBLE("PCM Playback Volume", 0, OPTi93X_DAC_LEFT, OPTi93X_DAC_RIGHT, 0, 0, 31, 1),
15691da177e4SLinus Torvalds OPTi93X_DOUBLE("FM Playback Switch", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 7, 7, 1, 1),
15701da177e4SLinus Torvalds OPTi93X_DOUBLE("FM Playback Volume", 0, OPTi931_FM_LEFT_INPUT, OPTi931_FM_RIGHT_INPUT, 1, 1, 15, 1),
15711da177e4SLinus Torvalds OPTi93X_DOUBLE("Line Playback Switch", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 7, 7, 1, 1),
15721da177e4SLinus Torvalds OPTi93X_DOUBLE("Line Playback Volume", 0, OPTi93X_LINE_LEFT_INPUT, OPTi93X_LINE_RIGHT_INPUT, 1, 1, 15, 1),
15731da177e4SLinus Torvalds OPTi93X_DOUBLE("Mic Playback Switch", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
15741da177e4SLinus Torvalds OPTi93X_DOUBLE("Mic Playback Volume", 0, OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1),
15751da177e4SLinus Torvalds OPTi93X_DOUBLE("Mic Boost", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 5, 5, 1, 1),
15761da177e4SLinus Torvalds OPTi93X_DOUBLE("CD Playback Switch", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 7, 7, 1, 1),
15771da177e4SLinus Torvalds OPTi93X_DOUBLE("CD Playback Volume", 0, OPTi93X_CD_LEFT_INPUT, OPTi93X_CD_RIGHT_INPUT, 1, 1, 15, 1),
15781da177e4SLinus Torvalds OPTi93X_DOUBLE("Aux Playback Switch", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
15791da177e4SLinus Torvalds OPTi93X_DOUBLE("Aux Playback Volume", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1),
15801da177e4SLinus Torvalds OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0, 0, 15, 0),
15811da177e4SLinus Torvalds {
15821da177e4SLinus Torvalds 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
15831da177e4SLinus Torvalds 	.name = "Capture Source",
15841da177e4SLinus Torvalds 	.info = snd_opti93x_info_mux,
15851da177e4SLinus Torvalds 	.get = snd_opti93x_get_mux,
15861da177e4SLinus Torvalds 	.put = snd_opti93x_put_mux,
15871da177e4SLinus Torvalds }
15881da177e4SLinus Torvalds };
15891da177e4SLinus Torvalds 
1590346c7a68STakashi Iwai static int snd_opti93x_mixer(struct snd_opti93x *chip)
15911da177e4SLinus Torvalds {
1592346c7a68STakashi Iwai 	struct snd_card *card;
1593346c7a68STakashi Iwai 	struct snd_kcontrol_new knew;
15941da177e4SLinus Torvalds 	int err;
15951da177e4SLinus Torvalds 	unsigned int idx;
15961da177e4SLinus Torvalds 
15971da177e4SLinus Torvalds 	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
15981da177e4SLinus Torvalds 
15991da177e4SLinus Torvalds 	card = chip->card;
16001da177e4SLinus Torvalds 
16011da177e4SLinus Torvalds 	strcpy(card->mixername, snd_opti93x_chip_id(chip));
16021da177e4SLinus Torvalds 
16031da177e4SLinus Torvalds 	for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
16041da177e4SLinus Torvalds 		knew = snd_opti93x_controls[idx];
16051da177e4SLinus Torvalds 		if (chip->hardware == OPTi9XX_HW_82C930) {
16061da177e4SLinus Torvalds 			if (strstr(knew.name, "FM"))	/* skip FM controls */
16071da177e4SLinus Torvalds 				continue;
16081da177e4SLinus Torvalds 			else if (strcmp(knew.name, "Mic Playback Volume"))
16091da177e4SLinus Torvalds 				OPTi93X_DOUBLE_INVERT_INVERT(knew);
16101da177e4SLinus Torvalds 			else if (strstr(knew.name, "Aux"))
16111da177e4SLinus Torvalds 				OPTi93X_DOUBLE_CHANGE_REGS(knew, OPTi930_AUX_LEFT_INPUT, OPTi930_AUX_RIGHT_INPUT);
16121da177e4SLinus Torvalds 			else if (strcmp(knew.name, "PCM Playback Volume"))
16131da177e4SLinus Torvalds 				OPTi93X_DOUBLE_INVERT_INVERT(knew);
16141da177e4SLinus Torvalds 			else if (strcmp(knew.name, "Master Playback Volume"))
16151da177e4SLinus Torvalds 				OPTi93X_DOUBLE_INVERT_INVERT(knew);
16161da177e4SLinus Torvalds 		}
16171da177e4SLinus Torvalds 		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opti93x_controls[idx], chip))) < 0)
16181da177e4SLinus Torvalds 			return err;
16191da177e4SLinus Torvalds 	}
16201da177e4SLinus Torvalds 	return 0;
16211da177e4SLinus Torvalds }
16221da177e4SLinus Torvalds 
16231da177e4SLinus Torvalds #endif /* OPTi93X */
16241da177e4SLinus Torvalds 
162599a0b768STakashi Iwai static int __init snd_card_opti9xx_detect(struct snd_card *card, struct snd_opti9xx *chip)
16261da177e4SLinus Torvalds {
16271da177e4SLinus Torvalds 	int i, err;
16281da177e4SLinus Torvalds 
16291da177e4SLinus Torvalds #ifndef OPTi93X
16301da177e4SLinus Torvalds 	for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {
16311da177e4SLinus Torvalds 		unsigned char value;
16321da177e4SLinus Torvalds 
16331da177e4SLinus Torvalds 		if ((err = snd_opti9xx_init(chip, i)) < 0)
16341da177e4SLinus Torvalds 			return err;
16351da177e4SLinus Torvalds 
16361da177e4SLinus Torvalds 		if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL)
16371da177e4SLinus Torvalds 			continue;
16381da177e4SLinus Torvalds 
16391da177e4SLinus Torvalds 		value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
16401da177e4SLinus Torvalds 		if ((value != 0xff) && (value != inb(chip->mc_base + 1)))
16411da177e4SLinus Torvalds 			if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
16421da177e4SLinus Torvalds 				return 1;
16431da177e4SLinus Torvalds 
1644b1d5776dSTakashi Iwai 		release_and_free_resource(chip->res_mc_base);
16451da177e4SLinus Torvalds 		chip->res_mc_base = NULL;
16461da177e4SLinus Torvalds 
16471da177e4SLinus Torvalds 	}
16481da177e4SLinus Torvalds #else	/* OPTi93X */
16491da177e4SLinus Torvalds 	for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {
16501da177e4SLinus Torvalds 		unsigned long flags;
16511da177e4SLinus Torvalds 		unsigned char value;
16521da177e4SLinus Torvalds 
16531da177e4SLinus Torvalds 		if ((err = snd_opti9xx_init(chip, i)) < 0)
16541da177e4SLinus Torvalds 			return err;
16551da177e4SLinus Torvalds 
16561da177e4SLinus Torvalds 		if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL)
16571da177e4SLinus Torvalds 			continue;
16581da177e4SLinus Torvalds 
16591da177e4SLinus Torvalds 		spin_lock_irqsave(&chip->lock, flags);
16601da177e4SLinus Torvalds 		outb(chip->password, chip->mc_base + chip->pwd_reg);
16611da177e4SLinus Torvalds 		outb(((chip->mc_indir_index & (1 << 8)) >> 4) |
16621da177e4SLinus Torvalds 			((chip->mc_indir_index & 0xf0) >> 4), chip->mc_base);
16631da177e4SLinus Torvalds 		spin_unlock_irqrestore(&chip->lock, flags);
16641da177e4SLinus Torvalds 
16651da177e4SLinus Torvalds 		value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));
16661da177e4SLinus Torvalds 		snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);
16671da177e4SLinus Torvalds 		if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)
16681da177e4SLinus Torvalds 			return 1;
16691da177e4SLinus Torvalds 
1670b1d5776dSTakashi Iwai 		release_and_free_resource(chip->res_mc_base);
16711da177e4SLinus Torvalds 		chip->res_mc_base = NULL;
16721da177e4SLinus Torvalds 	}
16731da177e4SLinus Torvalds #endif	/* OPTi93X */
16741da177e4SLinus Torvalds 
16751da177e4SLinus Torvalds 	return -ENODEV;
16761da177e4SLinus Torvalds }
16771da177e4SLinus Torvalds 
16781da177e4SLinus Torvalds #ifdef CONFIG_PNP
167999a0b768STakashi Iwai static int __init snd_card_opti9xx_pnp(struct snd_opti9xx *chip, struct pnp_card_link *card,
16801da177e4SLinus Torvalds 				       const struct pnp_card_device_id *pid)
16811da177e4SLinus Torvalds {
16821da177e4SLinus Torvalds 	struct pnp_dev *pdev;
16831da177e4SLinus Torvalds 	struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
16841da177e4SLinus Torvalds 	int err;
16851da177e4SLinus Torvalds 
16861da177e4SLinus Torvalds 	chip->dev = pnp_request_card_device(card, pid->devs[0].id, NULL);
16871da177e4SLinus Torvalds 	if (chip->dev == NULL) {
16881da177e4SLinus Torvalds 		kfree(cfg);
16891da177e4SLinus Torvalds 		return -EBUSY;
16901da177e4SLinus Torvalds 	}
16911da177e4SLinus Torvalds 	chip->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
16921da177e4SLinus Torvalds 
16931da177e4SLinus Torvalds 	pdev = chip->dev;
16941da177e4SLinus Torvalds 	pnp_init_resource_table(cfg);
16951da177e4SLinus Torvalds 
16961da177e4SLinus Torvalds #ifdef OPTi93X
16971da177e4SLinus Torvalds 	if (port != SNDRV_AUTO_PORT)
16981da177e4SLinus Torvalds 		pnp_resource_change(&cfg->port_resource[0], port + 4, 4);
16991da177e4SLinus Torvalds #else
17001da177e4SLinus Torvalds 	if (pid->driver_data != 0x0924 && port != SNDRV_AUTO_PORT)
17011da177e4SLinus Torvalds 		pnp_resource_change(&cfg->port_resource[1], port, 4);
17021da177e4SLinus Torvalds #endif	/* OPTi93X */
17031da177e4SLinus Torvalds 	if (irq != SNDRV_AUTO_IRQ)
17041da177e4SLinus Torvalds 		pnp_resource_change(&cfg->irq_resource[0], irq, 1);
17051da177e4SLinus Torvalds 	if (dma1 != SNDRV_AUTO_DMA)
17061da177e4SLinus Torvalds 		pnp_resource_change(&cfg->dma_resource[0], dma1, 1);
17071da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
17081da177e4SLinus Torvalds 	if (dma2 != SNDRV_AUTO_DMA)
17091da177e4SLinus Torvalds 		pnp_resource_change(&cfg->dma_resource[1], dma2, 1);
17101da177e4SLinus Torvalds #else
17111da177e4SLinus Torvalds #ifdef snd_opti9xx_fixup_dma2
17121da177e4SLinus Torvalds 	snd_opti9xx_fixup_dma2(pdev);
17131da177e4SLinus Torvalds #endif
17141da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
17151da177e4SLinus Torvalds #ifdef OPTi93X
17161da177e4SLinus Torvalds 	if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT)
17171da177e4SLinus Torvalds 		pnp_resource_change(&cfg->port_resource[1], fm_port, 4);
17181da177e4SLinus Torvalds #else
17191da177e4SLinus Torvalds 	if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT)
17201da177e4SLinus Torvalds 		pnp_resource_change(&cfg->port_resource[2], fm_port, 4);
17211da177e4SLinus Torvalds #endif
17221da177e4SLinus Torvalds 	if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
17231da177e4SLinus Torvalds 		snd_printk(KERN_ERR "AUDIO the requested resources are invalid, using auto config\n");
17241da177e4SLinus Torvalds 	err = pnp_activate_dev(pdev);
17251da177e4SLinus Torvalds 	if (err < 0) {
17261da177e4SLinus Torvalds 		snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
17271da177e4SLinus Torvalds 		kfree(cfg);
17281da177e4SLinus Torvalds 		return err;
17291da177e4SLinus Torvalds 	}
17301da177e4SLinus Torvalds 
17311da177e4SLinus Torvalds #ifdef OPTi93X
17321da177e4SLinus Torvalds 	port = pnp_port_start(pdev, 0) - 4;
17331da177e4SLinus Torvalds 	fm_port = pnp_port_start(pdev, 1);
17341da177e4SLinus Torvalds #else
17351da177e4SLinus Torvalds 	if (pid->driver_data != 0x0924)
17361da177e4SLinus Torvalds 		port = pnp_port_start(pdev, 1);
17371da177e4SLinus Torvalds 	fm_port = pnp_port_start(pdev, 2);
17381da177e4SLinus Torvalds #endif	/* OPTi93X */
17391da177e4SLinus Torvalds 	irq = pnp_irq(pdev, 0);
17401da177e4SLinus Torvalds 	dma1 = pnp_dma(pdev, 0);
17411da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
17421da177e4SLinus Torvalds 	dma2 = pnp_dma(pdev, 1);
17431da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
17441da177e4SLinus Torvalds 
17451da177e4SLinus Torvalds 	pdev = chip->devmpu;
17461da177e4SLinus Torvalds 	if (pdev && mpu_port > 0) {
17471da177e4SLinus Torvalds 		pnp_init_resource_table(cfg);
17481da177e4SLinus Torvalds 
17491da177e4SLinus Torvalds 		if (mpu_port != SNDRV_AUTO_PORT)
17501da177e4SLinus Torvalds 			pnp_resource_change(&cfg->port_resource[0], mpu_port, 2);
17511da177e4SLinus Torvalds 		if (mpu_irq != SNDRV_AUTO_IRQ)
17521da177e4SLinus Torvalds 			pnp_resource_change(&cfg->irq_resource[0], mpu_irq, 1);
17531da177e4SLinus Torvalds 
17541da177e4SLinus Torvalds 		if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
17551da177e4SLinus Torvalds 			snd_printk(KERN_ERR "AUDIO the requested resources are invalid, using auto config\n");
17561da177e4SLinus Torvalds 		err = pnp_activate_dev(pdev);
17571da177e4SLinus Torvalds 		if (err < 0) {
17581da177e4SLinus Torvalds 			snd_printk(KERN_ERR "AUDIO pnp configure failure\n");
17591da177e4SLinus Torvalds 			mpu_port = -1;
17601da177e4SLinus Torvalds 			chip->devmpu = NULL;
17611da177e4SLinus Torvalds 		} else {
17621da177e4SLinus Torvalds 			mpu_port = pnp_port_start(pdev, 0);
17631da177e4SLinus Torvalds 			mpu_irq = pnp_irq(pdev, 0);
17641da177e4SLinus Torvalds 		}
17651da177e4SLinus Torvalds 	}
17661da177e4SLinus Torvalds 	kfree(cfg);
17671da177e4SLinus Torvalds 	return pid->driver_data;
17681da177e4SLinus Torvalds }
17691da177e4SLinus Torvalds #endif	/* CONFIG_PNP */
17701da177e4SLinus Torvalds 
1771346c7a68STakashi Iwai static void snd_card_opti9xx_free(struct snd_card *card)
17721da177e4SLinus Torvalds {
177399a0b768STakashi Iwai 	struct snd_opti9xx *chip = card->private_data;
17741da177e4SLinus Torvalds 
1775b1d5776dSTakashi Iwai 	if (chip)
1776b1d5776dSTakashi Iwai 		release_and_free_resource(chip->res_mc_base);
17771da177e4SLinus Torvalds }
17781da177e4SLinus Torvalds 
177999a0b768STakashi Iwai static int __init snd_opti9xx_probe(struct snd_card *card)
17801da177e4SLinus Torvalds {
17811da177e4SLinus Torvalds 	static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
17821da177e4SLinus Torvalds 	int error;
178399a0b768STakashi Iwai 	struct snd_opti9xx *chip = card->private_data;
17841da177e4SLinus Torvalds #if defined(OPTi93X)
1785346c7a68STakashi Iwai 	struct snd_opti93x *codec;
17861da177e4SLinus Torvalds #elif defined(CS4231)
1787346c7a68STakashi Iwai 	struct snd_cs4231 *codec;
1788346c7a68STakashi Iwai 	struct snd_timer *timer;
17891da177e4SLinus Torvalds #else
1790346c7a68STakashi Iwai 	struct snd_ad1848 *codec;
17911da177e4SLinus Torvalds #endif
1792346c7a68STakashi Iwai 	struct snd_pcm *pcm;
1793346c7a68STakashi Iwai 	struct snd_rawmidi *rmidi;
1794346c7a68STakashi Iwai 	struct snd_hwdep *synth;
17951da177e4SLinus Torvalds 
17961da177e4SLinus Torvalds 	if (! chip->res_mc_base &&
179799a0b768STakashi Iwai 	    (chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
179899a0b768STakashi Iwai 						"OPTi9xx MC")) == NULL)
17991da177e4SLinus Torvalds 		return -ENOMEM;
18001da177e4SLinus Torvalds 
18011da177e4SLinus Torvalds 	chip->wss_base = port;
18021da177e4SLinus Torvalds 	chip->fm_port = fm_port;
18031da177e4SLinus Torvalds 	chip->mpu_port = mpu_port;
18041da177e4SLinus Torvalds 	chip->irq = irq;
18051da177e4SLinus Torvalds 	chip->mpu_irq = mpu_irq;
18061da177e4SLinus Torvalds 	chip->dma1 = dma1;
18071da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
18081da177e4SLinus Torvalds 	chip->dma2 = dma2;
18091da177e4SLinus Torvalds #endif
18101da177e4SLinus Torvalds 
18111da177e4SLinus Torvalds 	if (chip->wss_base == SNDRV_AUTO_PORT) {
18121da177e4SLinus Torvalds 		if ((chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) {
18131da177e4SLinus Torvalds 			snd_printk("unable to find a free WSS port\n");
18141da177e4SLinus Torvalds 			return -EBUSY;
18151da177e4SLinus Torvalds 		}
18161da177e4SLinus Torvalds 	}
181799a0b768STakashi Iwai 	if ((error = snd_opti9xx_configure(chip)))
18181da177e4SLinus Torvalds 		return error;
18191da177e4SLinus Torvalds 
18201da177e4SLinus Torvalds #if defined(OPTi93X)
182199a0b768STakashi Iwai 	if ((error = snd_opti93x_create(card, chip, chip->dma1, chip->dma2, &codec)))
18221da177e4SLinus Torvalds 		return error;
182399a0b768STakashi Iwai 	if ((error = snd_opti93x_pcm(codec, 0, &pcm)) < 0)
18241da177e4SLinus Torvalds 		return error;
182599a0b768STakashi Iwai 	if ((error = snd_opti93x_mixer(codec)) < 0)
18261da177e4SLinus Torvalds 		return error;
18271da177e4SLinus Torvalds #elif defined(CS4231)
18281da177e4SLinus Torvalds 	if ((error = snd_cs4231_create(card, chip->wss_base + 4, -1,
18291da177e4SLinus Torvalds 				       chip->irq, chip->dma1, chip->dma2,
18301da177e4SLinus Torvalds 				       CS4231_HW_DETECT,
18311da177e4SLinus Torvalds 				       0,
183299a0b768STakashi Iwai 				       &codec)) < 0)
18331da177e4SLinus Torvalds 		return error;
183499a0b768STakashi Iwai 	if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0)
18351da177e4SLinus Torvalds 		return error;
183699a0b768STakashi Iwai 	if ((error = snd_cs4231_mixer(codec)) < 0)
18371da177e4SLinus Torvalds 		return error;
183899a0b768STakashi Iwai 	if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0)
18391da177e4SLinus Torvalds 		return error;
18401da177e4SLinus Torvalds #else
18411da177e4SLinus Torvalds 	if ((error = snd_ad1848_create(card, chip->wss_base + 4,
18421da177e4SLinus Torvalds 				       chip->irq, chip->dma1,
184399a0b768STakashi Iwai 				       AD1848_HW_DETECT, &codec)) < 0)
18441da177e4SLinus Torvalds 		return error;
184599a0b768STakashi Iwai 	if ((error = snd_ad1848_pcm(codec, 0, &pcm)) < 0)
18461da177e4SLinus Torvalds 		return error;
184799a0b768STakashi Iwai 	if ((error = snd_ad1848_mixer(codec)) < 0)
18481da177e4SLinus Torvalds 		return error;
18491da177e4SLinus Torvalds #endif
18501da177e4SLinus Torvalds 	strcpy(card->driver, chip->name);
18511da177e4SLinus Torvalds 	sprintf(card->shortname, "OPTi %s", card->driver);
18521da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X)
18531da177e4SLinus Torvalds 	sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
18541da177e4SLinus Torvalds 		card->shortname, pcm->name, chip->wss_base + 4,
18551da177e4SLinus Torvalds 		chip->irq, chip->dma1, chip->dma2);
18561da177e4SLinus Torvalds #else
18571da177e4SLinus Torvalds 	sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
18581da177e4SLinus Torvalds 		card->shortname, pcm->name, chip->wss_base + 4,
18591da177e4SLinus Torvalds 		chip->irq, chip->dma1);
18601da177e4SLinus Torvalds #endif	/* CS4231 || OPTi93X */
18611da177e4SLinus Torvalds 
18621da177e4SLinus Torvalds 	if (chip->mpu_port <= 0 || chip->mpu_port == SNDRV_AUTO_PORT)
18631da177e4SLinus Torvalds 		rmidi = NULL;
18641da177e4SLinus Torvalds 	else
18651da177e4SLinus Torvalds 		if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
1866*65ca68b3SThomas Gleixner 				chip->mpu_port, 0, chip->mpu_irq, IRQF_DISABLED,
18671da177e4SLinus Torvalds 				&rmidi)))
186899a0b768STakashi Iwai 			snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
186999a0b768STakashi Iwai 				   chip->mpu_port);
18701da177e4SLinus Torvalds 
18711da177e4SLinus Torvalds 	if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) {
1872346c7a68STakashi Iwai 		struct snd_opl3 *opl3 = NULL;
18731da177e4SLinus Torvalds #ifndef OPTi93X
18741da177e4SLinus Torvalds 		if (chip->hardware == OPTi9XX_HW_82C928 ||
18751da177e4SLinus Torvalds 		    chip->hardware == OPTi9XX_HW_82C929 ||
18761da177e4SLinus Torvalds 		    chip->hardware == OPTi9XX_HW_82C924) {
1877346c7a68STakashi Iwai 			struct snd_opl4 *opl4;
18781da177e4SLinus Torvalds 			/* assume we have an OPL4 */
18791da177e4SLinus Torvalds 			snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
18801da177e4SLinus Torvalds 					       0x20, 0x20);
18811da177e4SLinus Torvalds 			if (snd_opl4_create(card,
18821da177e4SLinus Torvalds 					    chip->fm_port,
18831da177e4SLinus Torvalds 					    chip->fm_port - 8,
18841da177e4SLinus Torvalds 					    2, &opl3, &opl4) < 0) {
18851da177e4SLinus Torvalds 				/* no luck, use OPL3 instead */
18861da177e4SLinus Torvalds 				snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
18871da177e4SLinus Torvalds 						       0x00, 0x20);
18881da177e4SLinus Torvalds 			}
18891da177e4SLinus Torvalds 		}
18901da177e4SLinus Torvalds #endif	/* !OPTi93X */
18911da177e4SLinus Torvalds 		if (!opl3 && snd_opl3_create(card,
18921da177e4SLinus Torvalds 					     chip->fm_port,
18931da177e4SLinus Torvalds 					     chip->fm_port + 2,
18941da177e4SLinus Torvalds 					     OPL3_HW_AUTO, 0, &opl3) < 0) {
189599a0b768STakashi Iwai 			snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
18961da177e4SLinus Torvalds 				   chip->fm_port, chip->fm_port + 4 - 1);
18971da177e4SLinus Torvalds 		}
18981da177e4SLinus Torvalds 		if (opl3) {
18991da177e4SLinus Torvalds #ifdef CS4231
190099a0b768STakashi Iwai 			const int t1dev = 1;
19011da177e4SLinus Torvalds #else
190299a0b768STakashi Iwai 			const int t1dev = 0;
190399a0b768STakashi Iwai #endif
190499a0b768STakashi Iwai 			if ((error = snd_opl3_timer_new(opl3, t1dev, t1dev+1)) < 0)
19051da177e4SLinus Torvalds 				return error;
190699a0b768STakashi Iwai 			if ((error = snd_opl3_hwdep_new(opl3, 0, 1, &synth)) < 0)
19071da177e4SLinus Torvalds 				return error;
19081da177e4SLinus Torvalds 		}
19091da177e4SLinus Torvalds 	}
19101da177e4SLinus Torvalds 
191199a0b768STakashi Iwai 	return snd_card_register(card);
191299a0b768STakashi Iwai }
191399a0b768STakashi Iwai 
191499a0b768STakashi Iwai static struct snd_card *snd_opti9xx_card_new(void)
191599a0b768STakashi Iwai {
191699a0b768STakashi Iwai 	struct snd_card *card;
191799a0b768STakashi Iwai 
191899a0b768STakashi Iwai 	card = snd_card_new(index, id, THIS_MODULE, sizeof(struct snd_opti9xx));
191999a0b768STakashi Iwai 	if (! card)
192099a0b768STakashi Iwai 		return NULL;
192199a0b768STakashi Iwai 	card->private_free = snd_card_opti9xx_free;
192299a0b768STakashi Iwai 	return card;
192399a0b768STakashi Iwai }
192499a0b768STakashi Iwai 
192599a0b768STakashi Iwai static int __init snd_opti9xx_nonpnp_probe(struct platform_device *devptr)
192699a0b768STakashi Iwai {
192799a0b768STakashi Iwai 	struct snd_card *card;
192899a0b768STakashi Iwai 	int error;
192999a0b768STakashi Iwai 	static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1};
193099a0b768STakashi Iwai #ifdef OPTi93X
193199a0b768STakashi Iwai 	static int possible_irqs[] = {5, 9, 10, 11, 7, -1};
193299a0b768STakashi Iwai #else
193399a0b768STakashi Iwai 	static int possible_irqs[] = {9, 10, 11, 7, -1};
193499a0b768STakashi Iwai #endif	/* OPTi93X */
193599a0b768STakashi Iwai 	static int possible_mpu_irqs[] = {5, 9, 10, 7, -1};
193699a0b768STakashi Iwai 	static int possible_dma1s[] = {3, 1, 0, -1};
193799a0b768STakashi Iwai #if defined(CS4231) || defined(OPTi93X)
193899a0b768STakashi Iwai 	static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
193999a0b768STakashi Iwai #endif	/* CS4231 || OPTi93X */
194099a0b768STakashi Iwai 
194199a0b768STakashi Iwai 	if (snd_opti9xx_pnp_is_probed)
194299a0b768STakashi Iwai 		return -EBUSY;
194399a0b768STakashi Iwai 
194499a0b768STakashi Iwai 	if (mpu_port == SNDRV_AUTO_PORT) {
194599a0b768STakashi Iwai 		if ((mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) {
194699a0b768STakashi Iwai 			snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
194799a0b768STakashi Iwai 			return -EBUSY;
194899a0b768STakashi Iwai 		}
194999a0b768STakashi Iwai 	}
195099a0b768STakashi Iwai 	if (irq == SNDRV_AUTO_IRQ) {
195199a0b768STakashi Iwai 		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
195299a0b768STakashi Iwai 			snd_printk(KERN_ERR "unable to find a free IRQ\n");
195399a0b768STakashi Iwai 			return -EBUSY;
195499a0b768STakashi Iwai 		}
195599a0b768STakashi Iwai 	}
195699a0b768STakashi Iwai 	if (mpu_irq == SNDRV_AUTO_IRQ) {
195799a0b768STakashi Iwai 		if ((mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {
195899a0b768STakashi Iwai 			snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
195999a0b768STakashi Iwai 			return -EBUSY;
196099a0b768STakashi Iwai 		}
196199a0b768STakashi Iwai 	}
196299a0b768STakashi Iwai 	if (dma1 == SNDRV_AUTO_DMA) {
196399a0b768STakashi Iwai 		if ((dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {
196499a0b768STakashi Iwai 			snd_printk(KERN_ERR "unable to find a free DMA1\n");
196599a0b768STakashi Iwai 			return -EBUSY;
196699a0b768STakashi Iwai 		}
196799a0b768STakashi Iwai 	}
196899a0b768STakashi Iwai #if defined(CS4231) || defined(OPTi93X)
196999a0b768STakashi Iwai 	if (dma2 == SNDRV_AUTO_DMA) {
197099a0b768STakashi Iwai 		if ((dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4])) < 0) {
197199a0b768STakashi Iwai 			snd_printk("unable to find a free DMA2\n");
197299a0b768STakashi Iwai 			return -EBUSY;
197399a0b768STakashi Iwai 		}
197499a0b768STakashi Iwai 	}
197599a0b768STakashi Iwai #endif
197699a0b768STakashi Iwai 
197799a0b768STakashi Iwai 	card = snd_opti9xx_card_new();
197899a0b768STakashi Iwai 	if (! card)
197999a0b768STakashi Iwai 		return -ENOMEM;
198099a0b768STakashi Iwai 
198199a0b768STakashi Iwai 	if ((error = snd_card_opti9xx_detect(card, card->private_data)) < 0) {
19821da177e4SLinus Torvalds 		snd_card_free(card);
19831da177e4SLinus Torvalds 		return error;
19841da177e4SLinus Torvalds 	}
198599a0b768STakashi Iwai 	snd_card_set_dev(card, &devptr->dev);
198699a0b768STakashi Iwai 	if ((error = snd_opti9xx_probe(card)) < 0) {
198799a0b768STakashi Iwai 		snd_card_free(card);
198899a0b768STakashi Iwai 		return error;
198999a0b768STakashi Iwai 	}
199099a0b768STakashi Iwai 	platform_set_drvdata(devptr, card);
19911da177e4SLinus Torvalds 	return 0;
19921da177e4SLinus Torvalds }
19931da177e4SLinus Torvalds 
199499a0b768STakashi Iwai static int __devexit snd_opti9xx_nonpnp_remove(struct platform_device *devptr)
199599a0b768STakashi Iwai {
199699a0b768STakashi Iwai 	snd_card_free(platform_get_drvdata(devptr));
199799a0b768STakashi Iwai 	platform_set_drvdata(devptr, NULL);
199899a0b768STakashi Iwai 	return 0;
199999a0b768STakashi Iwai }
200099a0b768STakashi Iwai 
200199a0b768STakashi Iwai static struct platform_driver snd_opti9xx_driver = {
200299a0b768STakashi Iwai 	.probe		= snd_opti9xx_nonpnp_probe,
200399a0b768STakashi Iwai 	.remove		= __devexit_p(snd_opti9xx_nonpnp_remove),
200499a0b768STakashi Iwai 	/* FIXME: suspend/resume */
200599a0b768STakashi Iwai 	.driver		= {
200699a0b768STakashi Iwai 		.name	= DRIVER_NAME
200799a0b768STakashi Iwai 	},
200899a0b768STakashi Iwai };
200999a0b768STakashi Iwai 
20101da177e4SLinus Torvalds #ifdef CONFIG_PNP
201199a0b768STakashi Iwai static int __init snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
201299a0b768STakashi Iwai 					const struct pnp_card_device_id *pid)
201399a0b768STakashi Iwai {
201499a0b768STakashi Iwai 	struct snd_card *card;
201599a0b768STakashi Iwai 	int error, hw;
201699a0b768STakashi Iwai 	struct snd_opti9xx *chip;
201799a0b768STakashi Iwai 
201899a0b768STakashi Iwai 	if (snd_opti9xx_pnp_is_probed)
201999a0b768STakashi Iwai 		return -EBUSY;
202099a0b768STakashi Iwai 	if (! isapnp)
202199a0b768STakashi Iwai 		return -ENODEV;
202299a0b768STakashi Iwai 	card = snd_opti9xx_card_new();
202399a0b768STakashi Iwai 	if (! card)
202499a0b768STakashi Iwai 		return -ENOMEM;
202599a0b768STakashi Iwai 	chip = card->private_data;
202699a0b768STakashi Iwai 
202799a0b768STakashi Iwai 	hw = snd_card_opti9xx_pnp(chip, pcard, pid);
202899a0b768STakashi Iwai 	switch (hw) {
202999a0b768STakashi Iwai 	case 0x0924:
203099a0b768STakashi Iwai 		hw = OPTi9XX_HW_82C924;
203199a0b768STakashi Iwai 		break;
203299a0b768STakashi Iwai 	case 0x0925:
203399a0b768STakashi Iwai 		hw = OPTi9XX_HW_82C925;
203499a0b768STakashi Iwai 		break;
203599a0b768STakashi Iwai 	case 0x0931:
203699a0b768STakashi Iwai 		hw = OPTi9XX_HW_82C931;
203799a0b768STakashi Iwai 		break;
203899a0b768STakashi Iwai 	default:
203999a0b768STakashi Iwai 		snd_card_free(card);
204099a0b768STakashi Iwai 		return -ENODEV;
204199a0b768STakashi Iwai 	}
204299a0b768STakashi Iwai 
204399a0b768STakashi Iwai 	if ((error = snd_opti9xx_init(chip, hw))) {
204499a0b768STakashi Iwai 		snd_card_free(card);
204599a0b768STakashi Iwai 		return error;
204699a0b768STakashi Iwai 	}
204799a0b768STakashi Iwai 	if (hw <= OPTi9XX_HW_82C930)
204899a0b768STakashi Iwai 		chip->mc_base -= 0x80;
204999a0b768STakashi Iwai 	snd_card_set_dev(card, &pcard->card->dev);
205099a0b768STakashi Iwai 	if ((error = snd_opti9xx_probe(card)) < 0) {
205199a0b768STakashi Iwai 		snd_card_free(card);
205299a0b768STakashi Iwai 		return error;
205399a0b768STakashi Iwai 	}
205499a0b768STakashi Iwai 	pnp_set_card_drvdata(pcard, card);
205599a0b768STakashi Iwai 	snd_opti9xx_pnp_is_probed = 1;
205699a0b768STakashi Iwai 	return 0;
205799a0b768STakashi Iwai }
205899a0b768STakashi Iwai 
20591da177e4SLinus Torvalds static void __devexit snd_opti9xx_pnp_remove(struct pnp_card_link * pcard)
20601da177e4SLinus Torvalds {
206199a0b768STakashi Iwai 	snd_card_free(pnp_get_card_drvdata(pcard));
206299a0b768STakashi Iwai 	pnp_set_card_drvdata(pcard, NULL);
206399a0b768STakashi Iwai 	snd_opti9xx_pnp_is_probed = 0;
20641da177e4SLinus Torvalds }
20651da177e4SLinus Torvalds 
20661da177e4SLinus Torvalds static struct pnp_card_driver opti9xx_pnpc_driver = {
20671da177e4SLinus Torvalds 	.flags		= PNP_DRIVER_RES_DISABLE,
20681da177e4SLinus Torvalds 	.name		= "opti9xx",
20691da177e4SLinus Torvalds 	.id_table	= snd_opti9xx_pnpids,
207099a0b768STakashi Iwai 	.probe		= snd_opti9xx_pnp_probe,
20711da177e4SLinus Torvalds 	.remove		= __devexit_p(snd_opti9xx_pnp_remove),
20721da177e4SLinus Torvalds };
20731da177e4SLinus Torvalds #endif
20741da177e4SLinus Torvalds 
207599a0b768STakashi Iwai #ifdef CONFIG_PNP
207699a0b768STakashi Iwai #define is_isapnp_selected()	isapnp
207799a0b768STakashi Iwai #else
207899a0b768STakashi Iwai #define is_isapnp_selected()	0
207999a0b768STakashi Iwai #endif
208099a0b768STakashi Iwai #ifdef OPTi93X
208199a0b768STakashi Iwai #define CHIP_NAME	"82C93x"
208299a0b768STakashi Iwai #else
208399a0b768STakashi Iwai #define CHIP_NAME	"82C92x"
208499a0b768STakashi Iwai #endif
208599a0b768STakashi Iwai 
20861da177e4SLinus Torvalds static int __init alsa_card_opti9xx_init(void)
20871da177e4SLinus Torvalds {
208899a0b768STakashi Iwai 	int error;
208999a0b768STakashi Iwai 	struct platform_device *device;
20901da177e4SLinus Torvalds 
20910bbbc4caSTakashi Iwai #ifdef CONFIG_PNP
209299a0b768STakashi Iwai 	pnp_register_card_driver(&opti9xx_pnpc_driver);
209399a0b768STakashi Iwai 	if (snd_opti9xx_pnp_is_probed)
20941da177e4SLinus Torvalds 		return 0;
20950bbbc4caSTakashi Iwai #endif
209699a0b768STakashi Iwai 	if (! is_isapnp_selected()) {
209799a0b768STakashi Iwai 		error = platform_driver_register(&snd_opti9xx_driver);
209899a0b768STakashi Iwai 		if (error < 0)
209999a0b768STakashi Iwai 			return error;
210099a0b768STakashi Iwai 		device = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0);
2101f7a9275dSClemens Ladisch 		if (!IS_ERR(device)) {
2102dcccdd93SRene Herman 			if (platform_get_drvdata(device)) {
2103f7a9275dSClemens Ladisch 				snd_opti9xx_platform_device = device;
210499a0b768STakashi Iwai 				return 0;
2105f7a9275dSClemens Ladisch 			}
2106dcccdd93SRene Herman 			platform_device_unregister(device);
2107dcccdd93SRene Herman 		}
210899a0b768STakashi Iwai 		platform_driver_unregister(&snd_opti9xx_driver);
210999a0b768STakashi Iwai 	}
21100bbbc4caSTakashi Iwai #ifdef CONFIG_PNP
211199a0b768STakashi Iwai 	pnp_unregister_card_driver(&opti9xx_pnpc_driver);
21120bbbc4caSTakashi Iwai #endif
211399a0b768STakashi Iwai #ifdef MODULE
211499a0b768STakashi Iwai 	printk(KERN_ERR "no OPTi " CHIP_NAME " soundcard found\n");
211599a0b768STakashi Iwai #endif
211699a0b768STakashi Iwai 	return -ENODEV;
21171da177e4SLinus Torvalds }
21181da177e4SLinus Torvalds 
21191da177e4SLinus Torvalds static void __exit alsa_card_opti9xx_exit(void)
21201da177e4SLinus Torvalds {
2121f7a9275dSClemens Ladisch 	if (!snd_opti9xx_pnp_is_probed) {
2122f7a9275dSClemens Ladisch 		platform_device_unregister(snd_opti9xx_platform_device);
212399a0b768STakashi Iwai 		platform_driver_unregister(&snd_opti9xx_driver);
2124f7a9275dSClemens Ladisch 	}
21250bbbc4caSTakashi Iwai #ifdef CONFIG_PNP
21261da177e4SLinus Torvalds 	pnp_unregister_card_driver(&opti9xx_pnpc_driver);
21270bbbc4caSTakashi Iwai #endif
21281da177e4SLinus Torvalds }
21291da177e4SLinus Torvalds 
21301da177e4SLinus Torvalds module_init(alsa_card_opti9xx_init)
21311da177e4SLinus Torvalds module_exit(alsa_card_opti9xx_exit)
2132