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 { 1121da177e4SLinus Torvalds unsigned short hardware; 1131da177e4SLinus Torvalds unsigned char password; 1141da177e4SLinus Torvalds char name[7]; 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds unsigned long mc_base; 1171da177e4SLinus Torvalds struct resource *res_mc_base; 1181da177e4SLinus Torvalds unsigned long mc_base_size; 1191da177e4SLinus Torvalds #ifdef OPTi93X 1201da177e4SLinus Torvalds unsigned long mc_indir_index; 121e6960e19SKrzysztof Helt struct resource *res_mc_indir; 1221da177e4SLinus Torvalds #endif /* OPTi93X */ 1235dd25072SOndrej Zary struct snd_wss *codec; 1241da177e4SLinus Torvalds unsigned long pwd_reg; 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds spinlock_t lock; 1271da177e4SLinus Torvalds 128fd8d4735SKrzysztof Helt long wss_base; 1291da177e4SLinus Torvalds int irq; 1301da177e4SLinus Torvalds }; 1311da177e4SLinus Torvalds 13299a0b768STakashi Iwai static int snd_opti9xx_pnp_is_probed; 1331da177e4SLinus Torvalds 1341da177e4SLinus Torvalds #ifdef CONFIG_PNP 1351da177e4SLinus Torvalds 136529d2576SArvind Yadav static const struct pnp_card_device_id snd_opti9xx_pnpids[] = { 1371da177e4SLinus Torvalds #ifndef OPTi93X 1381da177e4SLinus Torvalds /* OPTi 82C924 */ 139fd8d4735SKrzysztof Helt { .id = "OPT0924", 140fd8d4735SKrzysztof Helt .devs = { { "OPT0000" }, { "OPT0002" }, { "OPT0005" } }, 141fd8d4735SKrzysztof Helt .driver_data = 0x0924 }, 1421da177e4SLinus Torvalds /* OPTi 82C925 */ 143fd8d4735SKrzysztof Helt { .id = "OPT0925", 144fd8d4735SKrzysztof Helt .devs = { { "OPT9250" }, { "OPT0002" }, { "OPT0005" } }, 145fd8d4735SKrzysztof Helt .driver_data = 0x0925 }, 1461da177e4SLinus Torvalds #else 1471da177e4SLinus Torvalds /* OPTi 82C931/3 */ 148fd8d4735SKrzysztof Helt { .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } }, 149fd8d4735SKrzysztof Helt .driver_data = 0x0931 }, 1501da177e4SLinus Torvalds #endif /* OPTi93X */ 1511da177e4SLinus Torvalds { .id = "" } 1521da177e4SLinus Torvalds }; 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids); 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds #endif /* CONFIG_PNP */ 1571da177e4SLinus Torvalds 158fb615499STakashi Iwai #define DEV_NAME KBUILD_MODNAME 1591da177e4SLinus Torvalds 16080e0a7c0STakashi Iwai static const char * const snd_opti9xx_names[] = { 161af901ca1SAndré Goddard Rosa "unknown", 1621da177e4SLinus Torvalds "82C928", "82C929", 1631da177e4SLinus Torvalds "82C924", "82C925", 1641da177e4SLinus Torvalds "82C930", "82C931", "82C933" 1651da177e4SLinus Torvalds }; 1661da177e4SLinus Torvalds 1671bff292eSBill Pemberton static int snd_opti9xx_init(struct snd_opti9xx *chip, 1685e24c1c1STakashi Iwai unsigned short hardware) 1691da177e4SLinus Torvalds { 17080e0a7c0STakashi Iwai static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; 1711da177e4SLinus Torvalds 1721da177e4SLinus Torvalds chip->hardware = hardware; 1731da177e4SLinus Torvalds strcpy(chip->name, snd_opti9xx_names[hardware]); 1741da177e4SLinus Torvalds 1751da177e4SLinus Torvalds spin_lock_init(&chip->lock); 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds chip->irq = -1; 1781da177e4SLinus Torvalds 179fd8d4735SKrzysztof Helt #ifndef OPTi93X 180fd8d4735SKrzysztof Helt #ifdef CONFIG_PNP 181fd8d4735SKrzysztof Helt if (isapnp && chip->mc_base) 182fd8d4735SKrzysztof Helt /* PnP resource gives the least 10 bits */ 183fd8d4735SKrzysztof Helt chip->mc_base |= 0xc00; 18489c0ac7cSRandy Dunlap else 185fd8d4735SKrzysztof Helt #endif /* CONFIG_PNP */ 18689c0ac7cSRandy Dunlap { 187fd8d4735SKrzysztof Helt chip->mc_base = 0xf8c; 188fd8d4735SKrzysztof Helt chip->mc_base_size = opti9xx_mc_size[hardware]; 189fd8d4735SKrzysztof Helt } 190fd8d4735SKrzysztof Helt #else 191fd8d4735SKrzysztof Helt chip->mc_base_size = opti9xx_mc_size[hardware]; 192fd8d4735SKrzysztof Helt #endif 193fd8d4735SKrzysztof Helt 1941da177e4SLinus Torvalds switch (hardware) { 1951da177e4SLinus Torvalds #ifndef OPTi93X 1961da177e4SLinus Torvalds case OPTi9XX_HW_82C928: 1971da177e4SLinus Torvalds case OPTi9XX_HW_82C929: 1981da177e4SLinus Torvalds chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3; 1991da177e4SLinus Torvalds chip->pwd_reg = 3; 2001da177e4SLinus Torvalds break; 2011da177e4SLinus Torvalds 2021da177e4SLinus Torvalds case OPTi9XX_HW_82C924: 2031da177e4SLinus Torvalds case OPTi9XX_HW_82C925: 2041da177e4SLinus Torvalds chip->password = 0xe5; 2051da177e4SLinus Torvalds chip->pwd_reg = 3; 2061da177e4SLinus Torvalds break; 2071da177e4SLinus Torvalds #else /* OPTi93X */ 2081da177e4SLinus Torvalds 2091da177e4SLinus Torvalds case OPTi9XX_HW_82C930: 2101da177e4SLinus Torvalds case OPTi9XX_HW_82C931: 2111da177e4SLinus Torvalds case OPTi9XX_HW_82C933: 2121da177e4SLinus Torvalds chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d; 21310a3061aSOndrej Zary if (!chip->mc_indir_index) 2141da177e4SLinus Torvalds chip->mc_indir_index = 0xe0e; 2151da177e4SLinus Torvalds chip->password = 0xe4; 2161da177e4SLinus Torvalds chip->pwd_reg = 0; 2171da177e4SLinus Torvalds break; 2181da177e4SLinus Torvalds #endif /* OPTi93X */ 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds default: 2214c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "chip %d not supported\n", hardware); 2221da177e4SLinus Torvalds return -ENODEV; 2231da177e4SLinus Torvalds } 2241da177e4SLinus Torvalds return 0; 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds 227346c7a68STakashi Iwai static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip, 2281da177e4SLinus Torvalds unsigned char reg) 2291da177e4SLinus Torvalds { 2301da177e4SLinus Torvalds unsigned long flags; 2311da177e4SLinus Torvalds unsigned char retval = 0xff; 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds spin_lock_irqsave(&chip->lock, flags); 2341da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds switch (chip->hardware) { 2371da177e4SLinus Torvalds #ifndef OPTi93X 2381da177e4SLinus Torvalds case OPTi9XX_HW_82C924: 2391da177e4SLinus Torvalds case OPTi9XX_HW_82C925: 2401da177e4SLinus Torvalds if (reg > 7) { 2411da177e4SLinus Torvalds outb(reg, chip->mc_base + 8); 2421da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 2431da177e4SLinus Torvalds retval = inb(chip->mc_base + 9); 2441da177e4SLinus Torvalds break; 2451da177e4SLinus Torvalds } 246c0dbbdadSGustavo A. R. Silva fallthrough; 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds case OPTi9XX_HW_82C928: 2491da177e4SLinus Torvalds case OPTi9XX_HW_82C929: 2501da177e4SLinus Torvalds retval = inb(chip->mc_base + reg); 2511da177e4SLinus Torvalds break; 2521da177e4SLinus Torvalds #else /* OPTi93X */ 2531da177e4SLinus Torvalds 2541da177e4SLinus Torvalds case OPTi9XX_HW_82C930: 2551da177e4SLinus Torvalds case OPTi9XX_HW_82C931: 2561da177e4SLinus Torvalds case OPTi9XX_HW_82C933: 2571da177e4SLinus Torvalds outb(reg, chip->mc_indir_index); 2581da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 2591da177e4SLinus Torvalds retval = inb(chip->mc_indir_index + 1); 2601da177e4SLinus Torvalds break; 2611da177e4SLinus Torvalds #endif /* OPTi93X */ 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds default: 2644c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 2651da177e4SLinus Torvalds } 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->lock, flags); 2681da177e4SLinus Torvalds return retval; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 271346c7a68STakashi Iwai static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg, 2721da177e4SLinus Torvalds unsigned char value) 2731da177e4SLinus Torvalds { 2741da177e4SLinus Torvalds unsigned long flags; 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds spin_lock_irqsave(&chip->lock, flags); 2771da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds switch (chip->hardware) { 2801da177e4SLinus Torvalds #ifndef OPTi93X 2811da177e4SLinus Torvalds case OPTi9XX_HW_82C924: 2821da177e4SLinus Torvalds case OPTi9XX_HW_82C925: 2831da177e4SLinus Torvalds if (reg > 7) { 2841da177e4SLinus Torvalds outb(reg, chip->mc_base + 8); 2851da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 2861da177e4SLinus Torvalds outb(value, chip->mc_base + 9); 2871da177e4SLinus Torvalds break; 2881da177e4SLinus Torvalds } 289c0dbbdadSGustavo A. R. Silva fallthrough; 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds case OPTi9XX_HW_82C928: 2921da177e4SLinus Torvalds case OPTi9XX_HW_82C929: 2931da177e4SLinus Torvalds outb(value, chip->mc_base + reg); 2941da177e4SLinus Torvalds break; 2951da177e4SLinus Torvalds #else /* OPTi93X */ 2961da177e4SLinus Torvalds 2971da177e4SLinus Torvalds case OPTi9XX_HW_82C930: 2981da177e4SLinus Torvalds case OPTi9XX_HW_82C931: 2991da177e4SLinus Torvalds case OPTi9XX_HW_82C933: 3001da177e4SLinus Torvalds outb(reg, chip->mc_indir_index); 3011da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 3021da177e4SLinus Torvalds outb(value, chip->mc_indir_index + 1); 3031da177e4SLinus Torvalds break; 3041da177e4SLinus Torvalds #endif /* OPTi93X */ 3051da177e4SLinus Torvalds 3061da177e4SLinus Torvalds default: 3074c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 3081da177e4SLinus Torvalds } 3091da177e4SLinus Torvalds 3101da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->lock, flags); 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds 3145ce00760SArnd Bergmann static inline void snd_opti9xx_write_mask(struct snd_opti9xx *chip, 3155ce00760SArnd Bergmann unsigned char reg, unsigned char value, unsigned char mask) 3165ce00760SArnd Bergmann { 3175ce00760SArnd Bergmann unsigned char oldval = snd_opti9xx_read(chip, reg); 3181da177e4SLinus Torvalds 3195ce00760SArnd Bergmann snd_opti9xx_write(chip, reg, (oldval & ~mask) | (value & mask)); 3205ce00760SArnd Bergmann } 3211da177e4SLinus Torvalds 32259b1f084STakashi Iwai static int snd_opti9xx_configure(struct snd_opti9xx *chip, 323fd8d4735SKrzysztof Helt long port, 324d8ea2393SKrzysztof Helt int irq, int dma1, int dma2, 325d8ea2393SKrzysztof Helt long mpu_port, int mpu_irq) 3261da177e4SLinus Torvalds { 3271da177e4SLinus Torvalds unsigned char wss_base_bits; 3281da177e4SLinus Torvalds unsigned char irq_bits; 3291da177e4SLinus Torvalds unsigned char dma_bits; 3301da177e4SLinus Torvalds unsigned char mpu_port_bits = 0; 3311da177e4SLinus Torvalds unsigned char mpu_irq_bits; 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds switch (chip->hardware) { 3341da177e4SLinus Torvalds #ifndef OPTi93X 3351da177e4SLinus Torvalds case OPTi9XX_HW_82C924: 336fd8d4735SKrzysztof Helt /* opti 929 mode (?), OPL3 clock output, audio enable */ 3371da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc); 338fd8d4735SKrzysztof Helt /* enable wave audio */ 3391da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02); 340c0dbbdadSGustavo A. R. Silva fallthrough; 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds case OPTi9XX_HW_82C925: 343fd8d4735SKrzysztof Helt /* enable WSS mode */ 3441da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); 345fd8d4735SKrzysztof Helt /* OPL3 FM synthesis */ 3461da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); 347fd8d4735SKrzysztof Helt /* disable Sound Blaster IRQ and DMA */ 3481da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff); 3491da177e4SLinus Torvalds #ifdef CS4231 350fd8d4735SKrzysztof Helt /* cs4231/4248 fix enabled */ 3511da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); 3521da177e4SLinus Torvalds #else 353fd8d4735SKrzysztof Helt /* cs4231/4248 fix disabled */ 3541da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); 3551da177e4SLinus Torvalds #endif /* CS4231 */ 3561da177e4SLinus Torvalds break; 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds case OPTi9XX_HW_82C928: 3591da177e4SLinus Torvalds case OPTi9XX_HW_82C929: 3601da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); 3611da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); 3621da177e4SLinus Torvalds /* 3631da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae); 3641da177e4SLinus Torvalds */ 3651da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c); 3661da177e4SLinus Torvalds #ifdef CS4231 3671da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); 3681da177e4SLinus Torvalds #else 3691da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); 3701da177e4SLinus Torvalds #endif /* CS4231 */ 3711da177e4SLinus Torvalds break; 3721da177e4SLinus Torvalds 3731da177e4SLinus Torvalds #else /* OPTi93X */ 3741da177e4SLinus Torvalds case OPTi9XX_HW_82C931: 37510a3061aSOndrej Zary /* disable 3D sound (set GPIO1 as output, low) */ 37610a3061aSOndrej Zary snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(20), 0x04, 0x0c); 377c0dbbdadSGustavo A. R. Silva fallthrough; 378c0dbbdadSGustavo A. R. Silva 37974ce5a46STakashi Iwai case OPTi9XX_HW_82C933: 380f81b953dSKrzysztof Helt /* 381f81b953dSKrzysztof Helt * The BTC 1817DW has QS1000 wavetable which is connected 382f81b953dSKrzysztof Helt * to the serial digital input of the OPTI931. 383f81b953dSKrzysztof Helt */ 384f81b953dSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(21), 0x82, 0xff); 385f81b953dSKrzysztof Helt /* 386f81b953dSKrzysztof Helt * This bit sets OPTI931 to automaticaly select FM 387f81b953dSKrzysztof Helt * or digital input signal. 388f81b953dSKrzysztof Helt */ 389f81b953dSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01); 390c0dbbdadSGustavo A. R. Silva fallthrough; 391c0dbbdadSGustavo A. R. Silva 39274ce5a46STakashi Iwai case OPTi9XX_HW_82C930: 3933ae5f36aSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03); 3943ae5f36aSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff); 3953ae5f36aSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 | 3963ae5f36aSKrzysztof Helt (chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04), 3973ae5f36aSKrzysztof Helt 0x34); 3983ae5f36aSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf); 3991da177e4SLinus Torvalds break; 4001da177e4SLinus Torvalds #endif /* OPTi93X */ 4011da177e4SLinus Torvalds 4021da177e4SLinus Torvalds default: 4034c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 4041da177e4SLinus Torvalds return -EINVAL; 4051da177e4SLinus Torvalds } 4061da177e4SLinus Torvalds 407fd8d4735SKrzysztof Helt /* PnP resource says it decodes only 10 bits of address */ 408fd8d4735SKrzysztof Helt switch (port & 0x3ff) { 409fd8d4735SKrzysztof Helt case 0x130: 410fd8d4735SKrzysztof Helt chip->wss_base = 0x530; 4111da177e4SLinus Torvalds wss_base_bits = 0x00; 4121da177e4SLinus Torvalds break; 413fd8d4735SKrzysztof Helt case 0x204: 414fd8d4735SKrzysztof Helt chip->wss_base = 0x604; 4151da177e4SLinus Torvalds wss_base_bits = 0x03; 4161da177e4SLinus Torvalds break; 417fd8d4735SKrzysztof Helt case 0x280: 418fd8d4735SKrzysztof Helt chip->wss_base = 0xe80; 4191da177e4SLinus Torvalds wss_base_bits = 0x01; 4201da177e4SLinus Torvalds break; 421fd8d4735SKrzysztof Helt case 0x340: 422fd8d4735SKrzysztof Helt chip->wss_base = 0xf40; 4231da177e4SLinus Torvalds wss_base_bits = 0x02; 4241da177e4SLinus Torvalds break; 4251da177e4SLinus Torvalds default: 426fd8d4735SKrzysztof Helt snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", port); 4271da177e4SLinus Torvalds goto __skip_base; 4281da177e4SLinus Torvalds } 4291da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30); 4301da177e4SLinus Torvalds 4311da177e4SLinus Torvalds __skip_base: 432d8ea2393SKrzysztof Helt switch (irq) { 4331da177e4SLinus Torvalds //#ifdef OPTi93X 4341da177e4SLinus Torvalds case 5: 4351da177e4SLinus Torvalds irq_bits = 0x05; 4361da177e4SLinus Torvalds break; 4371da177e4SLinus Torvalds //#endif /* OPTi93X */ 4381da177e4SLinus Torvalds case 7: 4391da177e4SLinus Torvalds irq_bits = 0x01; 4401da177e4SLinus Torvalds break; 4411da177e4SLinus Torvalds case 9: 4421da177e4SLinus Torvalds irq_bits = 0x02; 4431da177e4SLinus Torvalds break; 4441da177e4SLinus Torvalds case 10: 4451da177e4SLinus Torvalds irq_bits = 0x03; 4461da177e4SLinus Torvalds break; 4471da177e4SLinus Torvalds case 11: 4481da177e4SLinus Torvalds irq_bits = 0x04; 4491da177e4SLinus Torvalds break; 4501da177e4SLinus Torvalds default: 451d8ea2393SKrzysztof Helt snd_printk(KERN_WARNING "WSS irq # %d not valid\n", irq); 4521da177e4SLinus Torvalds goto __skip_resources; 4531da177e4SLinus Torvalds } 4541da177e4SLinus Torvalds 455d8ea2393SKrzysztof Helt switch (dma1) { 4561da177e4SLinus Torvalds case 0: 4571da177e4SLinus Torvalds dma_bits = 0x01; 4581da177e4SLinus Torvalds break; 4591da177e4SLinus Torvalds case 1: 4601da177e4SLinus Torvalds dma_bits = 0x02; 4611da177e4SLinus Torvalds break; 4621da177e4SLinus Torvalds case 3: 4631da177e4SLinus Torvalds dma_bits = 0x03; 4641da177e4SLinus Torvalds break; 4651da177e4SLinus Torvalds default: 466d8ea2393SKrzysztof Helt snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n", dma1); 4671da177e4SLinus Torvalds goto __skip_resources; 4681da177e4SLinus Torvalds } 4691da177e4SLinus Torvalds 4701da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X) 471d8ea2393SKrzysztof Helt if (dma1 == dma2) { 4724c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "don't want to share dmas\n"); 4731da177e4SLinus Torvalds return -EBUSY; 4741da177e4SLinus Torvalds } 4751da177e4SLinus Torvalds 476d8ea2393SKrzysztof Helt switch (dma2) { 4771da177e4SLinus Torvalds case 0: 4781da177e4SLinus Torvalds case 1: 4791da177e4SLinus Torvalds break; 4801da177e4SLinus Torvalds default: 481d8ea2393SKrzysztof Helt snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n", dma2); 4821da177e4SLinus Torvalds goto __skip_resources; 4831da177e4SLinus Torvalds } 4841da177e4SLinus Torvalds dma_bits |= 0x04; 4851da177e4SLinus Torvalds #endif /* CS4231 || OPTi93X */ 4861da177e4SLinus Torvalds 4871da177e4SLinus Torvalds #ifndef OPTi93X 488fd8d4735SKrzysztof Helt outb(irq_bits << 3 | dma_bits, chip->wss_base); 4891da177e4SLinus Torvalds #else /* OPTi93X */ 4901da177e4SLinus Torvalds snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits)); 4911da177e4SLinus Torvalds #endif /* OPTi93X */ 4921da177e4SLinus Torvalds 4931da177e4SLinus Torvalds __skip_resources: 4941da177e4SLinus Torvalds if (chip->hardware > OPTi9XX_HW_82C928) { 495d8ea2393SKrzysztof Helt switch (mpu_port) { 4961da177e4SLinus Torvalds case 0: 4971da177e4SLinus Torvalds case -1: 4981da177e4SLinus Torvalds break; 4991da177e4SLinus Torvalds case 0x300: 5001da177e4SLinus Torvalds mpu_port_bits = 0x03; 5011da177e4SLinus Torvalds break; 5021da177e4SLinus Torvalds case 0x310: 5031da177e4SLinus Torvalds mpu_port_bits = 0x02; 5041da177e4SLinus Torvalds break; 5051da177e4SLinus Torvalds case 0x320: 5061da177e4SLinus Torvalds mpu_port_bits = 0x01; 5071da177e4SLinus Torvalds break; 5081da177e4SLinus Torvalds case 0x330: 5091da177e4SLinus Torvalds mpu_port_bits = 0x00; 5101da177e4SLinus Torvalds break; 5111da177e4SLinus Torvalds default: 5124c9f1d3eSTakashi Iwai snd_printk(KERN_WARNING 513d8ea2393SKrzysztof Helt "MPU-401 port 0x%lx not valid\n", mpu_port); 5141da177e4SLinus Torvalds goto __skip_mpu; 5151da177e4SLinus Torvalds } 5161da177e4SLinus Torvalds 517d8ea2393SKrzysztof Helt switch (mpu_irq) { 5181da177e4SLinus Torvalds case 5: 5191da177e4SLinus Torvalds mpu_irq_bits = 0x02; 5201da177e4SLinus Torvalds break; 5211da177e4SLinus Torvalds case 7: 5221da177e4SLinus Torvalds mpu_irq_bits = 0x03; 5231da177e4SLinus Torvalds break; 5241da177e4SLinus Torvalds case 9: 5251da177e4SLinus Torvalds mpu_irq_bits = 0x00; 5261da177e4SLinus Torvalds break; 5271da177e4SLinus Torvalds case 10: 5281da177e4SLinus Torvalds mpu_irq_bits = 0x01; 5291da177e4SLinus Torvalds break; 5301da177e4SLinus Torvalds default: 5314c9f1d3eSTakashi Iwai snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n", 532d8ea2393SKrzysztof Helt mpu_irq); 5331da177e4SLinus Torvalds goto __skip_mpu; 5341da177e4SLinus Torvalds } 5351da177e4SLinus Torvalds 5361da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 537d8ea2393SKrzysztof Helt (mpu_port <= 0) ? 0x00 : 5381da177e4SLinus Torvalds 0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3, 5391da177e4SLinus Torvalds 0xf8); 5401da177e4SLinus Torvalds } 5411da177e4SLinus Torvalds __skip_mpu: 5421da177e4SLinus Torvalds 5431da177e4SLinus Torvalds return 0; 5441da177e4SLinus Torvalds } 5451da177e4SLinus Torvalds 5461da177e4SLinus Torvalds #ifdef OPTi93X 5471da177e4SLinus Torvalds 548e9d0a803SKrzysztof Helt static const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_step, -9300, 300, 0); 549e9d0a803SKrzysztof Helt static const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0); 550e9d0a803SKrzysztof Helt static const DECLARE_TLV_DB_SCALE(db_scale_4bit_12db_max, -3300, 300, 0); 551b2e8d7daSKrzysztof Helt 552fdd1f6fdSTakashi Iwai static const struct snd_kcontrol_new snd_opti93x_controls[] = { 553b2e8d7daSKrzysztof Helt WSS_DOUBLE("Master Playback Switch", 0, 554b2e8d7daSKrzysztof Helt OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1), 555b2e8d7daSKrzysztof Helt WSS_DOUBLE_TLV("Master Playback Volume", 0, 556b2e8d7daSKrzysztof Helt OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1, 557e9d0a803SKrzysztof Helt db_scale_5bit_3db_step), 558e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("PCM Playback Volume", 0, 559e9d0a803SKrzysztof Helt CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1, 560e9d0a803SKrzysztof Helt db_scale_5bit), 561e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("FM Playback Volume", 0, 562e9d0a803SKrzysztof Helt CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1, 563e9d0a803SKrzysztof Helt db_scale_4bit_12db_max), 564b2e8d7daSKrzysztof Helt WSS_DOUBLE("Line Playback Switch", 0, 565b2e8d7daSKrzysztof Helt CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), 566e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("Line Playback Volume", 0, 567e9d0a803SKrzysztof Helt CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1, 568e9d0a803SKrzysztof Helt db_scale_4bit_12db_max), 569b2e8d7daSKrzysztof Helt WSS_DOUBLE("Mic Playback Switch", 0, 570b2e8d7daSKrzysztof Helt OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1), 571e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("Mic Playback Volume", 0, 572e9d0a803SKrzysztof Helt OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1, 573e9d0a803SKrzysztof Helt db_scale_4bit_12db_max), 574e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("CD Playback Volume", 0, 575e9d0a803SKrzysztof Helt CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1, 576e9d0a803SKrzysztof Helt db_scale_4bit_12db_max), 577b2e8d7daSKrzysztof Helt WSS_DOUBLE("Aux Playback Switch", 0, 578b2e8d7daSKrzysztof Helt OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1), 579e9d0a803SKrzysztof Helt WSS_DOUBLE_TLV("Aux Playback Volume", 0, 580e9d0a803SKrzysztof Helt OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1, 581e9d0a803SKrzysztof Helt db_scale_4bit_12db_max), 582b2e8d7daSKrzysztof Helt }; 583b2e8d7daSKrzysztof Helt 5841bff292eSBill Pemberton static int snd_opti93x_mixer(struct snd_wss *chip) 585b2e8d7daSKrzysztof Helt { 586b2e8d7daSKrzysztof Helt struct snd_card *card; 587b2e8d7daSKrzysztof Helt unsigned int idx; 588b2e8d7daSKrzysztof Helt struct snd_ctl_elem_id id1, id2; 589b2e8d7daSKrzysztof Helt int err; 590b2e8d7daSKrzysztof Helt 591b2e8d7daSKrzysztof Helt if (snd_BUG_ON(!chip || !chip->pcm)) 592b2e8d7daSKrzysztof Helt return -EINVAL; 593b2e8d7daSKrzysztof Helt 594b2e8d7daSKrzysztof Helt card = chip->card; 595b2e8d7daSKrzysztof Helt 596b2e8d7daSKrzysztof Helt strcpy(card->mixername, chip->pcm->name); 597b2e8d7daSKrzysztof Helt 598b2e8d7daSKrzysztof Helt memset(&id1, 0, sizeof(id1)); 599b2e8d7daSKrzysztof Helt memset(&id2, 0, sizeof(id2)); 600b2e8d7daSKrzysztof Helt id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 601b2e8d7daSKrzysztof Helt /* reassign AUX0 switch to CD */ 602b2e8d7daSKrzysztof Helt strcpy(id1.name, "Aux Playback Switch"); 603b2e8d7daSKrzysztof Helt strcpy(id2.name, "CD Playback Switch"); 604b2e8d7daSKrzysztof Helt err = snd_ctl_rename_id(card, &id1, &id2); 605b2e8d7daSKrzysztof Helt if (err < 0) { 606b2e8d7daSKrzysztof Helt snd_printk(KERN_ERR "Cannot rename opti93x control\n"); 607b2e8d7daSKrzysztof Helt return err; 608b2e8d7daSKrzysztof Helt } 609b2e8d7daSKrzysztof Helt /* reassign AUX1 switch to FM */ 610b2e8d7daSKrzysztof Helt strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; 611b2e8d7daSKrzysztof Helt strcpy(id2.name, "FM Playback Switch"); 612b2e8d7daSKrzysztof Helt err = snd_ctl_rename_id(card, &id1, &id2); 613b2e8d7daSKrzysztof Helt if (err < 0) { 614b2e8d7daSKrzysztof Helt snd_printk(KERN_ERR "Cannot rename opti93x control\n"); 615b2e8d7daSKrzysztof Helt return err; 616b2e8d7daSKrzysztof Helt } 617b2e8d7daSKrzysztof Helt /* remove AUX1 volume */ 618b2e8d7daSKrzysztof Helt strcpy(id1.name, "Aux Playback Volume"); id1.index = 1; 619b2e8d7daSKrzysztof Helt snd_ctl_remove_id(card, &id1); 620b2e8d7daSKrzysztof Helt 621b2e8d7daSKrzysztof Helt /* Replace WSS volume controls with OPTi93x volume controls */ 622b2e8d7daSKrzysztof Helt id1.index = 0; 623b2e8d7daSKrzysztof Helt for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) { 624b2e8d7daSKrzysztof Helt strcpy(id1.name, snd_opti93x_controls[idx].name); 625b2e8d7daSKrzysztof Helt snd_ctl_remove_id(card, &id1); 626b2e8d7daSKrzysztof Helt 627b2e8d7daSKrzysztof Helt err = snd_ctl_add(card, 628b2e8d7daSKrzysztof Helt snd_ctl_new1(&snd_opti93x_controls[idx], chip)); 629b2e8d7daSKrzysztof Helt if (err < 0) 630b2e8d7daSKrzysztof Helt return err; 631b2e8d7daSKrzysztof Helt } 632b2e8d7daSKrzysztof Helt return 0; 633b2e8d7daSKrzysztof Helt } 634b2e8d7daSKrzysztof Helt 6357d12e780SDavid Howells static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id) 6361da177e4SLinus Torvalds { 6375f60e496SKrzysztof Helt struct snd_opti9xx *chip = dev_id; 6385f60e496SKrzysztof Helt struct snd_wss *codec = chip->codec; 6391da177e4SLinus Torvalds unsigned char status; 6401da177e4SLinus Torvalds 6415f60e496SKrzysztof Helt if (!codec) 6425f60e496SKrzysztof Helt return IRQ_HANDLED; 6435f60e496SKrzysztof Helt 6449f240a55SKrzysztof Helt status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11)); 6451da177e4SLinus Torvalds if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream) 6461da177e4SLinus Torvalds snd_pcm_period_elapsed(codec->playback_substream); 6471da177e4SLinus Torvalds if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) { 6487779f75fSKrzysztof Helt snd_wss_overrange(codec); 6491da177e4SLinus Torvalds snd_pcm_period_elapsed(codec->capture_substream); 6501da177e4SLinus Torvalds } 6511da177e4SLinus Torvalds outb(0x00, OPTi93X_PORT(codec, STATUS)); 6521da177e4SLinus Torvalds return IRQ_HANDLED; 6531da177e4SLinus Torvalds } 6541da177e4SLinus Torvalds 6551da177e4SLinus Torvalds #endif /* OPTi93X */ 6561da177e4SLinus Torvalds 6572973ee4aSTakashi Iwai static int snd_opti9xx_read_check(struct snd_card *card, 6582973ee4aSTakashi Iwai struct snd_opti9xx *chip) 659e6960e19SKrzysztof Helt { 660e6960e19SKrzysztof Helt unsigned char value; 661e6960e19SKrzysztof Helt #ifdef OPTi93X 662e6960e19SKrzysztof Helt unsigned long flags; 663e6960e19SKrzysztof Helt #endif 664e6960e19SKrzysztof Helt 6652973ee4aSTakashi Iwai chip->res_mc_base = 6662973ee4aSTakashi Iwai devm_request_region(card->dev, chip->mc_base, 6672973ee4aSTakashi Iwai chip->mc_base_size, "OPTi9xx MC"); 6682973ee4aSTakashi Iwai if (!chip->res_mc_base) 669e6960e19SKrzysztof Helt return -EBUSY; 670e6960e19SKrzysztof Helt #ifndef OPTi93X 671e6960e19SKrzysztof Helt value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)); 672e6960e19SKrzysztof Helt if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1))) 673e6960e19SKrzysztof Helt if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1))) 674e6960e19SKrzysztof Helt return 0; 675e6960e19SKrzysztof Helt #else /* OPTi93X */ 6762973ee4aSTakashi Iwai chip->res_mc_indir = 6772973ee4aSTakashi Iwai devm_request_region(card->dev, chip->mc_indir_index, 2, 678e6960e19SKrzysztof Helt "OPTi93x MC"); 6792973ee4aSTakashi Iwai if (!chip->res_mc_indir) 680e6960e19SKrzysztof Helt return -EBUSY; 681e6960e19SKrzysztof Helt 682e6960e19SKrzysztof Helt spin_lock_irqsave(&chip->lock, flags); 683e6960e19SKrzysztof Helt outb(chip->password, chip->mc_base + chip->pwd_reg); 684e6960e19SKrzysztof Helt outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base); 685e6960e19SKrzysztof Helt spin_unlock_irqrestore(&chip->lock, flags); 686e6960e19SKrzysztof Helt 687e6960e19SKrzysztof Helt value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)); 688e6960e19SKrzysztof Helt snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value); 689e6960e19SKrzysztof Helt if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value) 690e6960e19SKrzysztof Helt return 0; 691e6960e19SKrzysztof Helt 6922973ee4aSTakashi Iwai devm_release_resource(card->dev, chip->res_mc_indir); 693e6960e19SKrzysztof Helt chip->res_mc_indir = NULL; 694e6960e19SKrzysztof Helt #endif /* OPTi93X */ 6952973ee4aSTakashi Iwai devm_release_resource(card->dev, chip->res_mc_base); 696e6960e19SKrzysztof Helt chip->res_mc_base = NULL; 697e6960e19SKrzysztof Helt 698e6960e19SKrzysztof Helt return -ENODEV; 699e6960e19SKrzysztof Helt } 700e6960e19SKrzysztof Helt 7011bff292eSBill Pemberton static int snd_card_opti9xx_detect(struct snd_card *card, 7025e24c1c1STakashi Iwai struct snd_opti9xx *chip) 7031da177e4SLinus Torvalds { 7041da177e4SLinus Torvalds int i, err; 7051da177e4SLinus Torvalds 7061da177e4SLinus Torvalds #ifndef OPTi93X 7071da177e4SLinus Torvalds for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) { 708e6960e19SKrzysztof Helt #else 7091da177e4SLinus Torvalds for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) { 710e6960e19SKrzysztof Helt #endif 711e6960e19SKrzysztof Helt err = snd_opti9xx_init(chip, i); 712e6960e19SKrzysztof Helt if (err < 0) 7131da177e4SLinus Torvalds return err; 7141da177e4SLinus Torvalds 7152973ee4aSTakashi Iwai err = snd_opti9xx_read_check(card, chip); 716e6960e19SKrzysztof Helt if (err == 0) 7171da177e4SLinus Torvalds return 1; 718e6960e19SKrzysztof Helt #ifdef OPTi93X 719e6960e19SKrzysztof Helt chip->mc_indir_index = 0; 720e6960e19SKrzysztof Helt #endif 7211da177e4SLinus Torvalds } 7221da177e4SLinus Torvalds return -ENODEV; 7231da177e4SLinus Torvalds } 7241da177e4SLinus Torvalds 7251da177e4SLinus Torvalds #ifdef CONFIG_PNP 7261bff292eSBill Pemberton static int snd_card_opti9xx_pnp(struct snd_opti9xx *chip, 7275e24c1c1STakashi Iwai struct pnp_card_link *card, 7281da177e4SLinus Torvalds const struct pnp_card_device_id *pid) 7291da177e4SLinus Torvalds { 7301da177e4SLinus Torvalds struct pnp_dev *pdev; 7311da177e4SLinus Torvalds int err; 732fd8d4735SKrzysztof Helt struct pnp_dev *devmpu; 733fd8d4735SKrzysztof Helt #ifndef OPTi93X 734fd8d4735SKrzysztof Helt struct pnp_dev *devmc; 735fd8d4735SKrzysztof Helt #endif 7361da177e4SLinus Torvalds 737fd8d4735SKrzysztof Helt pdev = pnp_request_card_device(card, pid->devs[0].id, NULL); 738fd8d4735SKrzysztof Helt if (pdev == NULL) 7391da177e4SLinus Torvalds return -EBUSY; 740109c53f8SRene Herman 7411da177e4SLinus Torvalds err = pnp_activate_dev(pdev); 7421da177e4SLinus Torvalds if (err < 0) { 7431da177e4SLinus Torvalds snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err); 7441da177e4SLinus Torvalds return err; 7451da177e4SLinus Torvalds } 7461da177e4SLinus Torvalds 7471da177e4SLinus Torvalds #ifdef OPTi93X 7481da177e4SLinus Torvalds port = pnp_port_start(pdev, 0) - 4; 7491ea73412STakashi Iwai fm_port = pnp_port_start(pdev, 1) + 8; 75010a3061aSOndrej Zary /* adjust mc_indir_index - some cards report it at 0xe?d, 75110a3061aSOndrej Zary other at 0xe?c but it really is always at 0xe?e */ 75210a3061aSOndrej Zary chip->mc_indir_index = (pnp_port_start(pdev, 3) & ~0xf) | 0xe; 7531da177e4SLinus Torvalds #else 754fd8d4735SKrzysztof Helt devmc = pnp_request_card_device(card, pid->devs[2].id, NULL); 755fd8d4735SKrzysztof Helt if (devmc == NULL) 756fd8d4735SKrzysztof Helt return -EBUSY; 757fd8d4735SKrzysztof Helt 758fd8d4735SKrzysztof Helt err = pnp_activate_dev(devmc); 759fd8d4735SKrzysztof Helt if (err < 0) { 760fd8d4735SKrzysztof Helt snd_printk(KERN_ERR "MC pnp configure failure: %d\n", err); 761fd8d4735SKrzysztof Helt return err; 762fd8d4735SKrzysztof Helt } 763fd8d4735SKrzysztof Helt 7641da177e4SLinus Torvalds port = pnp_port_start(pdev, 1); 7651ea73412STakashi Iwai fm_port = pnp_port_start(pdev, 2) + 8; 766fd8d4735SKrzysztof Helt /* 767fd8d4735SKrzysztof Helt * The MC(0) is never accessed and card does not 768fd8d4735SKrzysztof Helt * include it in the PnP resource range. OPTI93x include it. 769fd8d4735SKrzysztof Helt */ 770fd8d4735SKrzysztof Helt chip->mc_base = pnp_port_start(devmc, 0) - 1; 771fd8d4735SKrzysztof Helt chip->mc_base_size = pnp_port_len(devmc, 0) + 1; 7721da177e4SLinus Torvalds #endif /* OPTi93X */ 7731da177e4SLinus Torvalds irq = pnp_irq(pdev, 0); 7741da177e4SLinus Torvalds dma1 = pnp_dma(pdev, 0); 7751da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X) 7761da177e4SLinus Torvalds dma2 = pnp_dma(pdev, 1); 7771da177e4SLinus Torvalds #endif /* CS4231 || OPTi93X */ 7781da177e4SLinus Torvalds 779fd8d4735SKrzysztof Helt devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL); 780fd8d4735SKrzysztof Helt 781fd8d4735SKrzysztof Helt if (devmpu && mpu_port > 0) { 782fd8d4735SKrzysztof Helt err = pnp_activate_dev(devmpu); 7831da177e4SLinus Torvalds if (err < 0) { 784fd8d4735SKrzysztof Helt snd_printk(KERN_ERR "MPU401 pnp configure failure\n"); 7851da177e4SLinus Torvalds mpu_port = -1; 7861da177e4SLinus Torvalds } else { 787fd8d4735SKrzysztof Helt mpu_port = pnp_port_start(devmpu, 0); 788fd8d4735SKrzysztof Helt mpu_irq = pnp_irq(devmpu, 0); 7891da177e4SLinus Torvalds } 7901da177e4SLinus Torvalds } 7911da177e4SLinus Torvalds return pid->driver_data; 7921da177e4SLinus Torvalds } 7931da177e4SLinus Torvalds #endif /* CONFIG_PNP */ 7941da177e4SLinus Torvalds 7951bff292eSBill Pemberton static int snd_opti9xx_probe(struct snd_card *card) 7961da177e4SLinus Torvalds { 79780e0a7c0STakashi Iwai static const long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1}; 7981da177e4SLinus Torvalds int error; 799d8ea2393SKrzysztof Helt int xdma2; 80099a0b768STakashi Iwai struct snd_opti9xx *chip = card->private_data; 8017779f75fSKrzysztof Helt struct snd_wss *codec; 802346c7a68STakashi Iwai struct snd_rawmidi *rmidi; 803346c7a68STakashi Iwai struct snd_hwdep *synth; 8041da177e4SLinus Torvalds 8051da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X) 806d8ea2393SKrzysztof Helt xdma2 = dma2; 807a0d9274cSRene Herman #else 808d8ea2393SKrzysztof Helt xdma2 = -1; 8091da177e4SLinus Torvalds #endif 8101da177e4SLinus Torvalds 811d8ea2393SKrzysztof Helt if (port == SNDRV_AUTO_PORT) { 812d8ea2393SKrzysztof Helt port = snd_legacy_find_free_ioport(possible_ports, 4); 813d8ea2393SKrzysztof Helt if (port < 0) { 8144c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "unable to find a free WSS port\n"); 8151da177e4SLinus Torvalds return -EBUSY; 8161da177e4SLinus Torvalds } 8171da177e4SLinus Torvalds } 818d8ea2393SKrzysztof Helt error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2, 819d8ea2393SKrzysztof Helt mpu_port, mpu_irq); 8207779f75fSKrzysztof Helt if (error) 8211da177e4SLinus Torvalds return error; 8221da177e4SLinus Torvalds 823fd8d4735SKrzysztof Helt error = snd_wss_create(card, chip->wss_base + 4, -1, irq, dma1, xdma2, 824a0d9274cSRene Herman #ifdef OPTi93X 8257779f75fSKrzysztof Helt WSS_HW_OPTI93X, WSS_HWSHARE_IRQ, 826a0d9274cSRene Herman #else 827a0d9274cSRene Herman WSS_HW_DETECT, 0, 8289f240a55SKrzysztof Helt #endif 8297779f75fSKrzysztof Helt &codec); 8307779f75fSKrzysztof Helt if (error < 0) 8311da177e4SLinus Torvalds return error; 8329f240a55SKrzysztof Helt chip->codec = codec; 833fa60c065SLars-Peter Clausen error = snd_wss_pcm(codec, 0); 8345664daa1SKrzysztof Helt if (error < 0) 8355664daa1SKrzysztof Helt return error; 8367779f75fSKrzysztof Helt error = snd_wss_mixer(codec); 8377779f75fSKrzysztof Helt if (error < 0) 8381da177e4SLinus Torvalds return error; 839b2e8d7daSKrzysztof Helt #ifdef OPTi93X 840b2e8d7daSKrzysztof Helt error = snd_opti93x_mixer(codec); 841b2e8d7daSKrzysztof Helt if (error < 0) 842b2e8d7daSKrzysztof Helt return error; 843b2e8d7daSKrzysztof Helt #endif 8449f240a55SKrzysztof Helt #ifdef CS4231 845fa60c065SLars-Peter Clausen error = snd_wss_timer(codec, 0); 8467779f75fSKrzysztof Helt if (error < 0) 8471da177e4SLinus Torvalds return error; 8485664daa1SKrzysztof Helt #endif 8495664daa1SKrzysztof Helt #ifdef OPTi93X 8502973ee4aSTakashi Iwai error = devm_request_irq(card->dev, irq, snd_opti93x_interrupt, 85188e24c3aSYong Zhang 0, DEV_NAME" - WSS", chip); 8529f240a55SKrzysztof Helt if (error < 0) { 8535f60e496SKrzysztof Helt snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", irq); 8549f240a55SKrzysztof Helt return error; 8559f240a55SKrzysztof Helt } 8569f240a55SKrzysztof Helt #endif 857d8ea2393SKrzysztof Helt chip->irq = irq; 85816d9fb1dSTakashi Iwai card->sync_irq = chip->irq; 8591da177e4SLinus Torvalds strcpy(card->driver, chip->name); 8601da177e4SLinus Torvalds sprintf(card->shortname, "OPTi %s", card->driver); 8611da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X) 86224d22077SArnd Bergmann snprintf(card->longname, sizeof(card->longname), 86324d22077SArnd Bergmann "%s, %s at 0x%lx, irq %d, dma %d&%d", 864fa60c065SLars-Peter Clausen card->shortname, codec->pcm->name, 865fd8d4735SKrzysztof Helt chip->wss_base + 4, irq, dma1, xdma2); 8661da177e4SLinus Torvalds #else 86724d22077SArnd Bergmann snprintf(card->longname, sizeof(card->longname), 86824d22077SArnd Bergmann "%s, %s at 0x%lx, irq %d, dma %d", 869fa60c065SLars-Peter Clausen card->shortname, codec->pcm->name, chip->wss_base + 4, irq, 870fa60c065SLars-Peter Clausen dma1); 8711da177e4SLinus Torvalds #endif /* CS4231 || OPTi93X */ 8721da177e4SLinus Torvalds 873d8ea2393SKrzysztof Helt if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT) 8741da177e4SLinus Torvalds rmidi = NULL; 875d8ea2393SKrzysztof Helt else { 876d8ea2393SKrzysztof Helt error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, 877dba8b469SClemens Ladisch mpu_port, 0, mpu_irq, &rmidi); 878d8ea2393SKrzysztof Helt if (error) 87999a0b768STakashi Iwai snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", 880d8ea2393SKrzysztof Helt mpu_port); 881d8ea2393SKrzysztof Helt } 8821da177e4SLinus Torvalds 883d8ea2393SKrzysztof Helt if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) { 884346c7a68STakashi Iwai struct snd_opl3 *opl3 = NULL; 8851da177e4SLinus Torvalds #ifndef OPTi93X 8861da177e4SLinus Torvalds if (chip->hardware == OPTi9XX_HW_82C928 || 8871da177e4SLinus Torvalds chip->hardware == OPTi9XX_HW_82C929 || 8881da177e4SLinus Torvalds chip->hardware == OPTi9XX_HW_82C924) { 889346c7a68STakashi Iwai struct snd_opl4 *opl4; 8901da177e4SLinus Torvalds /* assume we have an OPL4 */ 8911da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 8921da177e4SLinus Torvalds 0x20, 0x20); 893d8ea2393SKrzysztof Helt if (snd_opl4_create(card, fm_port, fm_port - 8, 8941da177e4SLinus Torvalds 2, &opl3, &opl4) < 0) { 8951da177e4SLinus Torvalds /* no luck, use OPL3 instead */ 8961da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 8971da177e4SLinus Torvalds 0x00, 0x20); 8981da177e4SLinus Torvalds } 8991da177e4SLinus Torvalds } 9001da177e4SLinus Torvalds #endif /* !OPTi93X */ 901d8ea2393SKrzysztof Helt if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2, 9021da177e4SLinus Torvalds OPL3_HW_AUTO, 0, &opl3) < 0) { 90399a0b768STakashi Iwai snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n", 904d8ea2393SKrzysztof Helt fm_port, fm_port + 4 - 1); 9051da177e4SLinus Torvalds } 9061da177e4SLinus Torvalds if (opl3) { 907aa9c293aSKrzysztof Helt error = snd_opl3_hwdep_new(opl3, 0, 1, &synth); 908aa9c293aSKrzysztof Helt if (error < 0) 9091da177e4SLinus Torvalds return error; 9101da177e4SLinus Torvalds } 9111da177e4SLinus Torvalds } 9121da177e4SLinus Torvalds 91399a0b768STakashi Iwai return snd_card_register(card); 91499a0b768STakashi Iwai } 91599a0b768STakashi Iwai 9164323cc4dSTakashi Iwai static int snd_opti9xx_card_new(struct device *pdev, struct snd_card **cardp) 91799a0b768STakashi Iwai { 91899a0b768STakashi Iwai struct snd_card *card; 919b1a0aac0STakashi Iwai int err; 92099a0b768STakashi Iwai 9212973ee4aSTakashi Iwai err = snd_devm_card_new(pdev, index, id, THIS_MODULE, 922c95eadd2STakashi Iwai sizeof(struct snd_opti9xx), &card); 923c95eadd2STakashi Iwai if (err < 0) 9243e7fb9f7STakashi Iwai return err; 9253e7fb9f7STakashi Iwai *cardp = card; 9263e7fb9f7STakashi Iwai return 0; 92799a0b768STakashi Iwai } 92899a0b768STakashi Iwai 9291bff292eSBill Pemberton static int snd_opti9xx_isa_match(struct device *devptr, 9305e24c1c1STakashi Iwai unsigned int dev) 9315e24c1c1STakashi Iwai { 932101f6f4bSTakashi Iwai #ifdef CONFIG_PNP 9335e24c1c1STakashi Iwai if (snd_opti9xx_pnp_is_probed) 9345e24c1c1STakashi Iwai return 0; 9355e24c1c1STakashi Iwai if (isapnp) 9365e24c1c1STakashi Iwai return 0; 937101f6f4bSTakashi Iwai #endif 9385e24c1c1STakashi Iwai return 1; 9395e24c1c1STakashi Iwai } 9405e24c1c1STakashi Iwai 9411bff292eSBill Pemberton static int snd_opti9xx_isa_probe(struct device *devptr, 9425e24c1c1STakashi Iwai unsigned int dev) 94399a0b768STakashi Iwai { 94499a0b768STakashi Iwai struct snd_card *card; 94599a0b768STakashi Iwai int error; 94680e0a7c0STakashi Iwai static const long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1}; 94799a0b768STakashi Iwai #ifdef OPTi93X 94880e0a7c0STakashi Iwai static const int possible_irqs[] = {5, 9, 10, 11, 7, -1}; 94999a0b768STakashi Iwai #else 95080e0a7c0STakashi Iwai static const int possible_irqs[] = {9, 10, 11, 7, -1}; 95199a0b768STakashi Iwai #endif /* OPTi93X */ 95280e0a7c0STakashi Iwai static const int possible_mpu_irqs[] = {5, 9, 10, 7, -1}; 95380e0a7c0STakashi Iwai static const int possible_dma1s[] = {3, 1, 0, -1}; 95499a0b768STakashi Iwai #if defined(CS4231) || defined(OPTi93X) 95580e0a7c0STakashi Iwai static const int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}}; 95699a0b768STakashi Iwai #endif /* CS4231 || OPTi93X */ 95799a0b768STakashi Iwai 95899a0b768STakashi Iwai if (mpu_port == SNDRV_AUTO_PORT) { 959913ad3a3STakashi Iwai mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2); 960913ad3a3STakashi Iwai if (mpu_port < 0) { 96199a0b768STakashi Iwai snd_printk(KERN_ERR "unable to find a free MPU401 port\n"); 96299a0b768STakashi Iwai return -EBUSY; 96399a0b768STakashi Iwai } 96499a0b768STakashi Iwai } 96599a0b768STakashi Iwai if (irq == SNDRV_AUTO_IRQ) { 966913ad3a3STakashi Iwai irq = snd_legacy_find_free_irq(possible_irqs); 967913ad3a3STakashi Iwai if (irq < 0) { 96899a0b768STakashi Iwai snd_printk(KERN_ERR "unable to find a free IRQ\n"); 96999a0b768STakashi Iwai return -EBUSY; 97099a0b768STakashi Iwai } 97199a0b768STakashi Iwai } 97299a0b768STakashi Iwai if (mpu_irq == SNDRV_AUTO_IRQ) { 973913ad3a3STakashi Iwai mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs); 974913ad3a3STakashi Iwai if (mpu_irq < 0) { 97599a0b768STakashi Iwai snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n"); 97699a0b768STakashi Iwai return -EBUSY; 97799a0b768STakashi Iwai } 97899a0b768STakashi Iwai } 97999a0b768STakashi Iwai if (dma1 == SNDRV_AUTO_DMA) { 980913ad3a3STakashi Iwai dma1 = snd_legacy_find_free_dma(possible_dma1s); 981913ad3a3STakashi Iwai if (dma1 < 0) { 98299a0b768STakashi Iwai snd_printk(KERN_ERR "unable to find a free DMA1\n"); 98399a0b768STakashi Iwai return -EBUSY; 98499a0b768STakashi Iwai } 98599a0b768STakashi Iwai } 98699a0b768STakashi Iwai #if defined(CS4231) || defined(OPTi93X) 98799a0b768STakashi Iwai if (dma2 == SNDRV_AUTO_DMA) { 988913ad3a3STakashi Iwai dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]); 989913ad3a3STakashi Iwai if (dma2 < 0) { 9904c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "unable to find a free DMA2\n"); 99199a0b768STakashi Iwai return -EBUSY; 99299a0b768STakashi Iwai } 99399a0b768STakashi Iwai } 99499a0b768STakashi Iwai #endif 99599a0b768STakashi Iwai 9964323cc4dSTakashi Iwai error = snd_opti9xx_card_new(devptr, &card); 9973e7fb9f7STakashi Iwai if (error < 0) 9983e7fb9f7STakashi Iwai return error; 99999a0b768STakashi Iwai 1000913ad3a3STakashi Iwai error = snd_card_opti9xx_detect(card, card->private_data); 10012973ee4aSTakashi Iwai if (error < 0) 10021da177e4SLinus Torvalds return error; 1003913ad3a3STakashi Iwai error = snd_opti9xx_probe(card); 10042973ee4aSTakashi Iwai if (error < 0) 100599a0b768STakashi Iwai return error; 10065e24c1c1STakashi Iwai dev_set_drvdata(devptr, card); 10071da177e4SLinus Torvalds return 0; 10081da177e4SLinus Torvalds } 10091da177e4SLinus Torvalds 10105dd25072SOndrej Zary #ifdef CONFIG_PM 10115dd25072SOndrej Zary static int snd_opti9xx_suspend(struct snd_card *card) 10125dd25072SOndrej Zary { 10135dd25072SOndrej Zary struct snd_opti9xx *chip = card->private_data; 10145dd25072SOndrej Zary 10155dd25072SOndrej Zary snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 10165dd25072SOndrej Zary chip->codec->suspend(chip->codec); 10175dd25072SOndrej Zary return 0; 10185dd25072SOndrej Zary } 10195dd25072SOndrej Zary 10205dd25072SOndrej Zary static int snd_opti9xx_resume(struct snd_card *card) 10215dd25072SOndrej Zary { 10225dd25072SOndrej Zary struct snd_opti9xx *chip = card->private_data; 10235dd25072SOndrej Zary int error, xdma2; 10245dd25072SOndrej Zary #if defined(CS4231) || defined(OPTi93X) 10255dd25072SOndrej Zary xdma2 = dma2; 10265dd25072SOndrej Zary #else 10275dd25072SOndrej Zary xdma2 = -1; 10285dd25072SOndrej Zary #endif 10295dd25072SOndrej Zary 10305dd25072SOndrej Zary error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2, 10315dd25072SOndrej Zary mpu_port, mpu_irq); 10325dd25072SOndrej Zary if (error) 10335dd25072SOndrej Zary return error; 10345dd25072SOndrej Zary chip->codec->resume(chip->codec); 10355dd25072SOndrej Zary snd_power_change_state(card, SNDRV_CTL_POWER_D0); 10365dd25072SOndrej Zary return 0; 10375dd25072SOndrej Zary } 10385dd25072SOndrej Zary 10395dd25072SOndrej Zary static int snd_opti9xx_isa_suspend(struct device *dev, unsigned int n, 10405dd25072SOndrej Zary pm_message_t state) 10415dd25072SOndrej Zary { 10425dd25072SOndrej Zary return snd_opti9xx_suspend(dev_get_drvdata(dev)); 10435dd25072SOndrej Zary } 10445dd25072SOndrej Zary 10455dd25072SOndrej Zary static int snd_opti9xx_isa_resume(struct device *dev, unsigned int n) 10465dd25072SOndrej Zary { 10475dd25072SOndrej Zary return snd_opti9xx_resume(dev_get_drvdata(dev)); 10485dd25072SOndrej Zary } 10495dd25072SOndrej Zary #endif 10505dd25072SOndrej Zary 10515e24c1c1STakashi Iwai static struct isa_driver snd_opti9xx_driver = { 10525e24c1c1STakashi Iwai .match = snd_opti9xx_isa_match, 10535e24c1c1STakashi Iwai .probe = snd_opti9xx_isa_probe, 10545dd25072SOndrej Zary #ifdef CONFIG_PM 10555dd25072SOndrej Zary .suspend = snd_opti9xx_isa_suspend, 10565dd25072SOndrej Zary .resume = snd_opti9xx_isa_resume, 10575dd25072SOndrej Zary #endif 105899a0b768STakashi Iwai .driver = { 105983c51c0aSRene Herman .name = DEV_NAME 106099a0b768STakashi Iwai }, 106199a0b768STakashi Iwai }; 106299a0b768STakashi Iwai 10631da177e4SLinus Torvalds #ifdef CONFIG_PNP 10641bff292eSBill Pemberton static int snd_opti9xx_pnp_probe(struct pnp_card_link *pcard, 106599a0b768STakashi Iwai const struct pnp_card_device_id *pid) 106699a0b768STakashi Iwai { 106799a0b768STakashi Iwai struct snd_card *card; 106899a0b768STakashi Iwai int error, hw; 106999a0b768STakashi Iwai struct snd_opti9xx *chip; 107099a0b768STakashi Iwai 107199a0b768STakashi Iwai if (snd_opti9xx_pnp_is_probed) 107299a0b768STakashi Iwai return -EBUSY; 107399a0b768STakashi Iwai if (! isapnp) 107499a0b768STakashi Iwai return -ENODEV; 10754323cc4dSTakashi Iwai error = snd_opti9xx_card_new(&pcard->card->dev, &card); 10763e7fb9f7STakashi Iwai if (error < 0) 10773e7fb9f7STakashi Iwai return error; 107899a0b768STakashi Iwai chip = card->private_data; 107999a0b768STakashi Iwai 108099a0b768STakashi Iwai hw = snd_card_opti9xx_pnp(chip, pcard, pid); 108199a0b768STakashi Iwai switch (hw) { 108299a0b768STakashi Iwai case 0x0924: 108399a0b768STakashi Iwai hw = OPTi9XX_HW_82C924; 108499a0b768STakashi Iwai break; 108599a0b768STakashi Iwai case 0x0925: 108699a0b768STakashi Iwai hw = OPTi9XX_HW_82C925; 108799a0b768STakashi Iwai break; 108899a0b768STakashi Iwai case 0x0931: 108999a0b768STakashi Iwai hw = OPTi9XX_HW_82C931; 109099a0b768STakashi Iwai break; 109199a0b768STakashi Iwai default: 109299a0b768STakashi Iwai return -ENODEV; 109399a0b768STakashi Iwai } 109499a0b768STakashi Iwai 1095913ad3a3STakashi Iwai error = snd_opti9xx_init(chip, hw); 10962973ee4aSTakashi Iwai if (error) 109799a0b768STakashi Iwai return error; 10982973ee4aSTakashi Iwai error = snd_opti9xx_read_check(card, chip); 1099*e24ef488SColin Ian King if (error) { 1100e6960e19SKrzysztof Helt snd_printk(KERN_ERR "OPTI chip not found\n"); 1101e6960e19SKrzysztof Helt return error; 1102*e24ef488SColin Ian King } 1103913ad3a3STakashi Iwai error = snd_opti9xx_probe(card); 11042973ee4aSTakashi Iwai if (error < 0) 110599a0b768STakashi Iwai return error; 110699a0b768STakashi Iwai pnp_set_card_drvdata(pcard, card); 110799a0b768STakashi Iwai snd_opti9xx_pnp_is_probed = 1; 110899a0b768STakashi Iwai return 0; 110999a0b768STakashi Iwai } 111099a0b768STakashi Iwai 11111bff292eSBill Pemberton static void snd_opti9xx_pnp_remove(struct pnp_card_link *pcard) 11121da177e4SLinus Torvalds { 111399a0b768STakashi Iwai snd_opti9xx_pnp_is_probed = 0; 11141da177e4SLinus Torvalds } 11151da177e4SLinus Torvalds 11165dd25072SOndrej Zary #ifdef CONFIG_PM 11175dd25072SOndrej Zary static int snd_opti9xx_pnp_suspend(struct pnp_card_link *pcard, 11185dd25072SOndrej Zary pm_message_t state) 11195dd25072SOndrej Zary { 11205dd25072SOndrej Zary return snd_opti9xx_suspend(pnp_get_card_drvdata(pcard)); 11215dd25072SOndrej Zary } 11225dd25072SOndrej Zary 11235dd25072SOndrej Zary static int snd_opti9xx_pnp_resume(struct pnp_card_link *pcard) 11245dd25072SOndrej Zary { 11255dd25072SOndrej Zary return snd_opti9xx_resume(pnp_get_card_drvdata(pcard)); 11265dd25072SOndrej Zary } 11275dd25072SOndrej Zary #endif 11285dd25072SOndrej Zary 11291da177e4SLinus Torvalds static struct pnp_card_driver opti9xx_pnpc_driver = { 11301da177e4SLinus Torvalds .flags = PNP_DRIVER_RES_DISABLE, 1131fb615499STakashi Iwai .name = DEV_NAME, 11321da177e4SLinus Torvalds .id_table = snd_opti9xx_pnpids, 113399a0b768STakashi Iwai .probe = snd_opti9xx_pnp_probe, 11341bff292eSBill Pemberton .remove = snd_opti9xx_pnp_remove, 11355dd25072SOndrej Zary #ifdef CONFIG_PM 11365dd25072SOndrej Zary .suspend = snd_opti9xx_pnp_suspend, 11375dd25072SOndrej Zary .resume = snd_opti9xx_pnp_resume, 11385dd25072SOndrej Zary #endif 11391da177e4SLinus Torvalds }; 11401da177e4SLinus Torvalds #endif 11411da177e4SLinus Torvalds 114299a0b768STakashi Iwai #ifdef OPTi93X 114399a0b768STakashi Iwai #define CHIP_NAME "82C93x" 114499a0b768STakashi Iwai #else 114599a0b768STakashi Iwai #define CHIP_NAME "82C92x" 114699a0b768STakashi Iwai #endif 114799a0b768STakashi Iwai 11481da177e4SLinus Torvalds static int __init alsa_card_opti9xx_init(void) 11491da177e4SLinus Torvalds { 11500bbbc4caSTakashi Iwai #ifdef CONFIG_PNP 115199a0b768STakashi Iwai pnp_register_card_driver(&opti9xx_pnpc_driver); 115299a0b768STakashi Iwai if (snd_opti9xx_pnp_is_probed) 11531da177e4SLinus Torvalds return 0; 1154101f6f4bSTakashi Iwai pnp_unregister_card_driver(&opti9xx_pnpc_driver); 11550bbbc4caSTakashi Iwai #endif 11565e24c1c1STakashi Iwai return isa_register_driver(&snd_opti9xx_driver, 1); 11571da177e4SLinus Torvalds } 11581da177e4SLinus Torvalds 11591da177e4SLinus Torvalds static void __exit alsa_card_opti9xx_exit(void) 11601da177e4SLinus Torvalds { 1161f7a9275dSClemens Ladisch if (!snd_opti9xx_pnp_is_probed) { 11625e24c1c1STakashi Iwai isa_unregister_driver(&snd_opti9xx_driver); 11635e24c1c1STakashi Iwai return; 1164f7a9275dSClemens Ladisch } 11650bbbc4caSTakashi Iwai #ifdef CONFIG_PNP 11661da177e4SLinus Torvalds pnp_unregister_card_driver(&opti9xx_pnpc_driver); 11670bbbc4caSTakashi Iwai #endif 11681da177e4SLinus Torvalds } 11691da177e4SLinus Torvalds 11701da177e4SLinus Torvalds module_init(alsa_card_opti9xx_init) 11711da177e4SLinus Torvalds module_exit(alsa_card_opti9xx_exit) 1172