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