11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards. 31da177e4SLinus Torvalds Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it> 41da177e4SLinus Torvalds 51da177e4SLinus Torvalds Part of this code was developed at the Italian Ministry of Air Defence, 61da177e4SLinus Torvalds Sixth Division (oh, che pace ...), Rome. 71da177e4SLinus Torvalds 81da177e4SLinus Torvalds Thanks to Maria Grazia Pollarini, Salvatore Vassallo. 91da177e4SLinus Torvalds 101da177e4SLinus Torvalds This program is free software; you can redistribute it and/or modify 111da177e4SLinus Torvalds it under the terms of the GNU General Public License as published by 121da177e4SLinus Torvalds the Free Software Foundation; either version 2 of the License, or 131da177e4SLinus Torvalds (at your option) any later version. 141da177e4SLinus Torvalds 151da177e4SLinus Torvalds This program is distributed in the hope that it will be useful, 161da177e4SLinus Torvalds but WITHOUT ANY WARRANTY; without even the implied warranty of 171da177e4SLinus Torvalds MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 181da177e4SLinus Torvalds GNU General Public License for more details. 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds You should have received a copy of the GNU General Public License 211da177e4SLinus Torvalds along with this program; if not, write to the Free Software 221da177e4SLinus Torvalds Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 231da177e4SLinus Torvalds */ 241da177e4SLinus Torvalds 251da177e4SLinus Torvalds 261da177e4SLinus Torvalds #include <linux/init.h> 2799a0b768STakashi Iwai #include <linux/err.h> 285e24c1c1STakashi Iwai #include <linux/isa.h> 2999a0b768STakashi Iwai #include <linux/delay.h> 301da177e4SLinus Torvalds #include <linux/slab.h> 311da177e4SLinus Torvalds #include <linux/pnp.h> 321da177e4SLinus Torvalds #include <linux/moduleparam.h> 3399a0b768STakashi Iwai #include <asm/io.h> 3499a0b768STakashi Iwai #include <asm/dma.h> 351da177e4SLinus Torvalds #include <sound/core.h> 3661ef19d7SKrzysztof Helt #include <sound/wss.h> 371da177e4SLinus Torvalds #include <sound/mpu401.h> 381da177e4SLinus Torvalds #include <sound/opl3.h> 391da177e4SLinus Torvalds #ifndef OPTi93X 401da177e4SLinus Torvalds #include <sound/opl4.h> 411da177e4SLinus Torvalds #endif 421da177e4SLinus Torvalds #define SNDRV_LEGACY_FIND_FREE_IRQ 431da177e4SLinus Torvalds #define SNDRV_LEGACY_FIND_FREE_DMA 441da177e4SLinus Torvalds #include <sound/initval.h> 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); 471da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 481da177e4SLinus Torvalds #ifdef OPTi93X 491da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi93X"); 501da177e4SLinus Torvalds MODULE_SUPPORTED_DEVICE("{{OPTi,82C931/3}}"); 511da177e4SLinus Torvalds #else /* OPTi93X */ 521da177e4SLinus Torvalds #ifdef CS4231 531da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi92X - CS4231"); 541da177e4SLinus Torvalds MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (CS4231)}," 551da177e4SLinus Torvalds "{OPTi,82C925 (CS4231)}}"); 561da177e4SLinus Torvalds #else /* CS4231 */ 571da177e4SLinus Torvalds MODULE_DESCRIPTION("OPTi92X - AD1848"); 581da177e4SLinus Torvalds MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)}," 591da177e4SLinus Torvalds "{OPTi,82C925 (AD1848)}," 601da177e4SLinus Torvalds "{OAK,Mozart}}"); 611da177e4SLinus Torvalds #endif /* CS4231 */ 621da177e4SLinus Torvalds #endif /* OPTi93X */ 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ 651da177e4SLinus Torvalds static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ 661da177e4SLinus Torvalds //static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */ 6751f6baadSRene Herman #ifdef CONFIG_PNP 681da177e4SLinus Torvalds static int isapnp = 1; /* Enable ISA PnP detection */ 6951f6baadSRene Herman #endif 701da177e4SLinus Torvalds static long port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */ 711da177e4SLinus Torvalds static long mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */ 721da177e4SLinus Torvalds static long fm_port = SNDRV_DEFAULT_PORT1; /* 0x388 */ 731da177e4SLinus Torvalds static int irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10,11 */ 741da177e4SLinus Torvalds static int mpu_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10 */ 751da177e4SLinus Torvalds static int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ 761da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X) 771da177e4SLinus Torvalds static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ 781da177e4SLinus Torvalds #endif /* CS4231 || OPTi93X */ 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds module_param(index, int, 0444); 811da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for opti9xx based soundcard."); 821da177e4SLinus Torvalds module_param(id, charp, 0444); 831da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard."); 841da177e4SLinus Torvalds //module_param(enable, bool, 0444); 851da177e4SLinus Torvalds //MODULE_PARM_DESC(enable, "Enable opti9xx soundcard."); 8651f6baadSRene Herman #ifdef CONFIG_PNP 871da177e4SLinus Torvalds module_param(isapnp, bool, 0444); 881da177e4SLinus Torvalds MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard."); 8951f6baadSRene Herman #endif 901da177e4SLinus Torvalds module_param(port, long, 0444); 911da177e4SLinus Torvalds MODULE_PARM_DESC(port, "WSS port # for opti9xx driver."); 921da177e4SLinus Torvalds module_param(mpu_port, long, 0444); 931da177e4SLinus Torvalds MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver."); 941da177e4SLinus Torvalds module_param(fm_port, long, 0444); 951da177e4SLinus Torvalds MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver."); 961da177e4SLinus Torvalds module_param(irq, int, 0444); 971da177e4SLinus Torvalds MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver."); 981da177e4SLinus Torvalds module_param(mpu_irq, int, 0444); 991da177e4SLinus Torvalds MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver."); 1001da177e4SLinus Torvalds module_param(dma1, int, 0444); 1011da177e4SLinus Torvalds MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver."); 1021da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X) 1031da177e4SLinus Torvalds module_param(dma2, int, 0444); 1041da177e4SLinus Torvalds MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver."); 1051da177e4SLinus Torvalds #endif /* CS4231 || OPTi93X */ 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds #define OPTi9XX_HW_82C928 1 1081da177e4SLinus Torvalds #define OPTi9XX_HW_82C929 2 1091da177e4SLinus Torvalds #define OPTi9XX_HW_82C924 3 1101da177e4SLinus Torvalds #define OPTi9XX_HW_82C925 4 1111da177e4SLinus Torvalds #define OPTi9XX_HW_82C930 5 1121da177e4SLinus Torvalds #define OPTi9XX_HW_82C931 6 1131da177e4SLinus Torvalds #define OPTi9XX_HW_82C933 7 1141da177e4SLinus Torvalds #define OPTi9XX_HW_LAST OPTi9XX_HW_82C933 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds #define OPTi9XX_MC_REG(n) n 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds #ifdef OPTi93X 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds #define OPTi93X_STATUS 0x02 1211da177e4SLinus Torvalds #define OPTi93X_PORT(chip, r) ((chip)->port + OPTi93X_##r) 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds #define OPTi93X_IRQ_PLAYBACK 0x04 1241da177e4SLinus Torvalds #define OPTi93X_IRQ_CAPTURE 0x08 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds #endif /* OPTi93X */ 1271da177e4SLinus Torvalds 128346c7a68STakashi Iwai struct snd_opti9xx { 1291da177e4SLinus Torvalds unsigned short hardware; 1301da177e4SLinus Torvalds unsigned char password; 1311da177e4SLinus Torvalds char name[7]; 1321da177e4SLinus Torvalds 1331da177e4SLinus Torvalds unsigned long mc_base; 1341da177e4SLinus Torvalds struct resource *res_mc_base; 1351da177e4SLinus Torvalds unsigned long mc_base_size; 1361da177e4SLinus Torvalds #ifdef OPTi93X 1371da177e4SLinus Torvalds unsigned long mc_indir_index; 138e6960e19SKrzysztof Helt unsigned long mc_indir_size; 139e6960e19SKrzysztof Helt struct resource *res_mc_indir; 1407779f75fSKrzysztof Helt struct snd_wss *codec; 1411da177e4SLinus Torvalds #endif /* OPTi93X */ 1421da177e4SLinus Torvalds unsigned long pwd_reg; 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds spinlock_t lock; 1451da177e4SLinus Torvalds 1461da177e4SLinus Torvalds int irq; 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds #ifdef CONFIG_PNP 1491da177e4SLinus Torvalds struct pnp_dev *dev; 1501da177e4SLinus Torvalds struct pnp_dev *devmpu; 1511da177e4SLinus Torvalds #endif /* CONFIG_PNP */ 1521da177e4SLinus Torvalds }; 1531da177e4SLinus Torvalds 15499a0b768STakashi Iwai static int snd_opti9xx_pnp_is_probed; 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds #ifdef CONFIG_PNP 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds static struct pnp_card_device_id snd_opti9xx_pnpids[] = { 1591da177e4SLinus Torvalds #ifndef OPTi93X 1601da177e4SLinus Torvalds /* OPTi 82C924 */ 1611da177e4SLinus Torvalds { .id = "OPT0924", .devs = { { "OPT0000" }, { "OPT0002" } }, .driver_data = 0x0924 }, 1621da177e4SLinus Torvalds /* OPTi 82C925 */ 1631da177e4SLinus Torvalds { .id = "OPT0925", .devs = { { "OPT9250" }, { "OPT0002" } }, .driver_data = 0x0925 }, 1641da177e4SLinus Torvalds #else 1651da177e4SLinus Torvalds /* OPTi 82C931/3 */ 1661da177e4SLinus Torvalds { .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } }, .driver_data = 0x0931 }, 1671da177e4SLinus Torvalds #endif /* OPTi93X */ 1681da177e4SLinus Torvalds { .id = "" } 1691da177e4SLinus Torvalds }; 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids); 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds #endif /* CONFIG_PNP */ 1741da177e4SLinus Torvalds 1751da177e4SLinus Torvalds #ifdef OPTi93X 17683c51c0aSRene Herman #define DEV_NAME "opti93x" 1771da177e4SLinus Torvalds #else 17883c51c0aSRene Herman #define DEV_NAME "opti92x" 17983c51c0aSRene Herman #endif 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds static char * snd_opti9xx_names[] = { 1821da177e4SLinus Torvalds "unkown", 1831da177e4SLinus Torvalds "82C928", "82C929", 1841da177e4SLinus Torvalds "82C924", "82C925", 1851da177e4SLinus Torvalds "82C930", "82C931", "82C933" 1861da177e4SLinus Torvalds }; 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds 1895e24c1c1STakashi Iwai static long __devinit snd_legacy_find_free_ioport(long *port_table, long size) 1901da177e4SLinus Torvalds { 1911da177e4SLinus Torvalds while (*port_table != -1) { 192b1d5776dSTakashi Iwai if (request_region(*port_table, size, "ALSA test")) { 193b1d5776dSTakashi Iwai release_region(*port_table, size); 1941da177e4SLinus Torvalds return *port_table; 1951da177e4SLinus Torvalds } 1961da177e4SLinus Torvalds port_table++; 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds return -1; 1991da177e4SLinus Torvalds } 2001da177e4SLinus Torvalds 2015e24c1c1STakashi Iwai static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip, 2025e24c1c1STakashi Iwai unsigned short hardware) 2031da177e4SLinus Torvalds { 2041da177e4SLinus Torvalds static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds chip->hardware = hardware; 2071da177e4SLinus Torvalds strcpy(chip->name, snd_opti9xx_names[hardware]); 2081da177e4SLinus Torvalds 2091da177e4SLinus Torvalds chip->mc_base_size = opti9xx_mc_size[hardware]; 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds spin_lock_init(&chip->lock); 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds chip->irq = -1; 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds switch (hardware) { 2161da177e4SLinus Torvalds #ifndef OPTi93X 2171da177e4SLinus Torvalds case OPTi9XX_HW_82C928: 2181da177e4SLinus Torvalds case OPTi9XX_HW_82C929: 2191da177e4SLinus Torvalds chip->mc_base = 0xf8c; 2201da177e4SLinus Torvalds chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3; 2211da177e4SLinus Torvalds chip->pwd_reg = 3; 2221da177e4SLinus Torvalds break; 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds case OPTi9XX_HW_82C924: 2251da177e4SLinus Torvalds case OPTi9XX_HW_82C925: 2261da177e4SLinus Torvalds chip->mc_base = 0xf8c; 2271da177e4SLinus Torvalds chip->password = 0xe5; 2281da177e4SLinus Torvalds chip->pwd_reg = 3; 2291da177e4SLinus Torvalds break; 2301da177e4SLinus Torvalds #else /* OPTi93X */ 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds case OPTi9XX_HW_82C930: 2331da177e4SLinus Torvalds case OPTi9XX_HW_82C931: 2341da177e4SLinus Torvalds case OPTi9XX_HW_82C933: 2351da177e4SLinus Torvalds chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d; 236e6960e19SKrzysztof Helt if (!chip->mc_indir_index) { 2371da177e4SLinus Torvalds chip->mc_indir_index = 0xe0e; 238e6960e19SKrzysztof Helt chip->mc_indir_size = 2; 239e6960e19SKrzysztof Helt } 2401da177e4SLinus Torvalds chip->password = 0xe4; 2411da177e4SLinus Torvalds chip->pwd_reg = 0; 2421da177e4SLinus Torvalds break; 2431da177e4SLinus Torvalds #endif /* OPTi93X */ 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds default: 2464c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "chip %d not supported\n", hardware); 2471da177e4SLinus Torvalds return -ENODEV; 2481da177e4SLinus Torvalds } 2491da177e4SLinus Torvalds return 0; 2501da177e4SLinus Torvalds } 2511da177e4SLinus Torvalds 252346c7a68STakashi Iwai static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip, 2531da177e4SLinus Torvalds unsigned char reg) 2541da177e4SLinus Torvalds { 2551da177e4SLinus Torvalds unsigned long flags; 2561da177e4SLinus Torvalds unsigned char retval = 0xff; 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds spin_lock_irqsave(&chip->lock, flags); 2591da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds switch (chip->hardware) { 2621da177e4SLinus Torvalds #ifndef OPTi93X 2631da177e4SLinus Torvalds case OPTi9XX_HW_82C924: 2641da177e4SLinus Torvalds case OPTi9XX_HW_82C925: 2651da177e4SLinus Torvalds if (reg > 7) { 2661da177e4SLinus Torvalds outb(reg, chip->mc_base + 8); 2671da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 2681da177e4SLinus Torvalds retval = inb(chip->mc_base + 9); 2691da177e4SLinus Torvalds break; 2701da177e4SLinus Torvalds } 2711da177e4SLinus Torvalds 2721da177e4SLinus Torvalds case OPTi9XX_HW_82C928: 2731da177e4SLinus Torvalds case OPTi9XX_HW_82C929: 2741da177e4SLinus Torvalds retval = inb(chip->mc_base + reg); 2751da177e4SLinus Torvalds break; 2761da177e4SLinus Torvalds #else /* OPTi93X */ 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds case OPTi9XX_HW_82C930: 2791da177e4SLinus Torvalds case OPTi9XX_HW_82C931: 2801da177e4SLinus Torvalds case OPTi9XX_HW_82C933: 2811da177e4SLinus Torvalds outb(reg, chip->mc_indir_index); 2821da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 2831da177e4SLinus Torvalds retval = inb(chip->mc_indir_index + 1); 2841da177e4SLinus Torvalds break; 2851da177e4SLinus Torvalds #endif /* OPTi93X */ 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds default: 2884c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 2891da177e4SLinus Torvalds } 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->lock, flags); 2921da177e4SLinus Torvalds return retval; 2931da177e4SLinus Torvalds } 2941da177e4SLinus Torvalds 295346c7a68STakashi Iwai static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg, 2961da177e4SLinus Torvalds unsigned char value) 2971da177e4SLinus Torvalds { 2981da177e4SLinus Torvalds unsigned long flags; 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds spin_lock_irqsave(&chip->lock, flags); 3011da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 3021da177e4SLinus Torvalds 3031da177e4SLinus Torvalds switch (chip->hardware) { 3041da177e4SLinus Torvalds #ifndef OPTi93X 3051da177e4SLinus Torvalds case OPTi9XX_HW_82C924: 3061da177e4SLinus Torvalds case OPTi9XX_HW_82C925: 3071da177e4SLinus Torvalds if (reg > 7) { 3081da177e4SLinus Torvalds outb(reg, chip->mc_base + 8); 3091da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 3101da177e4SLinus Torvalds outb(value, chip->mc_base + 9); 3111da177e4SLinus Torvalds break; 3121da177e4SLinus Torvalds } 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds case OPTi9XX_HW_82C928: 3151da177e4SLinus Torvalds case OPTi9XX_HW_82C929: 3161da177e4SLinus Torvalds outb(value, chip->mc_base + reg); 3171da177e4SLinus Torvalds break; 3181da177e4SLinus Torvalds #else /* OPTi93X */ 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds case OPTi9XX_HW_82C930: 3211da177e4SLinus Torvalds case OPTi9XX_HW_82C931: 3221da177e4SLinus Torvalds case OPTi9XX_HW_82C933: 3231da177e4SLinus Torvalds outb(reg, chip->mc_indir_index); 3241da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 3251da177e4SLinus Torvalds outb(value, chip->mc_indir_index + 1); 3261da177e4SLinus Torvalds break; 3271da177e4SLinus Torvalds #endif /* OPTi93X */ 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds default: 3304c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 3311da177e4SLinus Torvalds } 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->lock, flags); 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds #define snd_opti9xx_write_mask(chip, reg, value, mask) \ 3381da177e4SLinus Torvalds snd_opti9xx_write(chip, reg, \ 3391da177e4SLinus Torvalds (snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask))) 3401da177e4SLinus Torvalds 3411da177e4SLinus Torvalds 342d8ea2393SKrzysztof Helt static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip, 343d8ea2393SKrzysztof Helt long wss_base, 344d8ea2393SKrzysztof Helt int irq, int dma1, int dma2, 345d8ea2393SKrzysztof Helt long mpu_port, int mpu_irq) 3461da177e4SLinus Torvalds { 3471da177e4SLinus Torvalds unsigned char wss_base_bits; 3481da177e4SLinus Torvalds unsigned char irq_bits; 3491da177e4SLinus Torvalds unsigned char dma_bits; 3501da177e4SLinus Torvalds unsigned char mpu_port_bits = 0; 3511da177e4SLinus Torvalds unsigned char mpu_irq_bits; 3521da177e4SLinus Torvalds 3531da177e4SLinus Torvalds switch (chip->hardware) { 3541da177e4SLinus Torvalds #ifndef OPTi93X 3551da177e4SLinus Torvalds case OPTi9XX_HW_82C924: 3561da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc); 3571da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02); 3581da177e4SLinus Torvalds 3591da177e4SLinus Torvalds case OPTi9XX_HW_82C925: 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 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff); 3631da177e4SLinus Torvalds #ifdef CS4231 3641da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); 3651da177e4SLinus Torvalds #else 3661da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); 3671da177e4SLinus Torvalds #endif /* CS4231 */ 3681da177e4SLinus Torvalds break; 3691da177e4SLinus Torvalds 3701da177e4SLinus Torvalds case OPTi9XX_HW_82C928: 3711da177e4SLinus Torvalds case OPTi9XX_HW_82C929: 3721da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); 3731da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); 3741da177e4SLinus Torvalds /* 3751da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae); 3761da177e4SLinus Torvalds */ 3771da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c); 3781da177e4SLinus Torvalds #ifdef CS4231 3791da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); 3801da177e4SLinus Torvalds #else 3811da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); 3821da177e4SLinus Torvalds #endif /* CS4231 */ 3831da177e4SLinus Torvalds break; 3841da177e4SLinus Torvalds 3851da177e4SLinus Torvalds #else /* OPTi93X */ 3861da177e4SLinus Torvalds case OPTi9XX_HW_82C931: 3871da177e4SLinus Torvalds case OPTi9XX_HW_82C933: 388f81b953dSKrzysztof Helt /* 389f81b953dSKrzysztof Helt * The BTC 1817DW has QS1000 wavetable which is connected 390f81b953dSKrzysztof Helt * to the serial digital input of the OPTI931. 391f81b953dSKrzysztof Helt */ 392f81b953dSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(21), 0x82, 0xff); 393f81b953dSKrzysztof Helt /* 394f81b953dSKrzysztof Helt * This bit sets OPTI931 to automaticaly select FM 395f81b953dSKrzysztof Helt * or digital input signal. 396f81b953dSKrzysztof Helt */ 397f81b953dSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01); 3983ae5f36aSKrzysztof Helt case OPTi9XX_HW_82C930: /* FALL THROUGH */ 3993ae5f36aSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03); 4003ae5f36aSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff); 4013ae5f36aSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 | 4023ae5f36aSKrzysztof Helt (chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04), 4033ae5f36aSKrzysztof Helt 0x34); 4043ae5f36aSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf); 4051da177e4SLinus Torvalds break; 4061da177e4SLinus Torvalds #endif /* OPTi93X */ 4071da177e4SLinus Torvalds 4081da177e4SLinus Torvalds default: 4094c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 4101da177e4SLinus Torvalds return -EINVAL; 4111da177e4SLinus Torvalds } 4121da177e4SLinus Torvalds 413d8ea2393SKrzysztof Helt switch (wss_base) { 4141da177e4SLinus Torvalds case 0x530: 4151da177e4SLinus Torvalds wss_base_bits = 0x00; 4161da177e4SLinus Torvalds break; 4171da177e4SLinus Torvalds case 0x604: 4181da177e4SLinus Torvalds wss_base_bits = 0x03; 4191da177e4SLinus Torvalds break; 4201da177e4SLinus Torvalds case 0xe80: 4211da177e4SLinus Torvalds wss_base_bits = 0x01; 4221da177e4SLinus Torvalds break; 4231da177e4SLinus Torvalds case 0xf40: 4241da177e4SLinus Torvalds wss_base_bits = 0x02; 4251da177e4SLinus Torvalds break; 4261da177e4SLinus Torvalds default: 427d8ea2393SKrzysztof Helt snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", wss_base); 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: 452d8ea2393SKrzysztof Helt snd_printk(KERN_WARNING "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: 467d8ea2393SKrzysztof Helt snd_printk(KERN_WARNING "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) { 4734c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "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: 482d8ea2393SKrzysztof Helt snd_printk(KERN_WARNING "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 489d8ea2393SKrzysztof Helt outb(irq_bits << 3 | dma_bits, 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: 5134c9f1d3eSTakashi Iwai snd_printk(KERN_WARNING 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: 5324c9f1d3eSTakashi Iwai snd_printk(KERN_WARNING "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 5497d12e780SDavid Howells static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id) 5501da177e4SLinus Torvalds { 551*5f60e496SKrzysztof Helt struct snd_opti9xx *chip = dev_id; 552*5f60e496SKrzysztof Helt struct snd_wss *codec = chip->codec; 5531da177e4SLinus Torvalds unsigned char status; 5541da177e4SLinus Torvalds 555*5f60e496SKrzysztof Helt if (!codec) 556*5f60e496SKrzysztof Helt return IRQ_HANDLED; 557*5f60e496SKrzysztof Helt 5589f240a55SKrzysztof Helt status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11)); 5591da177e4SLinus Torvalds if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream) 5601da177e4SLinus Torvalds snd_pcm_period_elapsed(codec->playback_substream); 5611da177e4SLinus Torvalds if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) { 5627779f75fSKrzysztof Helt snd_wss_overrange(codec); 5631da177e4SLinus Torvalds snd_pcm_period_elapsed(codec->capture_substream); 5641da177e4SLinus Torvalds } 5651da177e4SLinus Torvalds outb(0x00, OPTi93X_PORT(codec, STATUS)); 5661da177e4SLinus Torvalds return IRQ_HANDLED; 5671da177e4SLinus Torvalds } 5681da177e4SLinus Torvalds 5691da177e4SLinus Torvalds #endif /* OPTi93X */ 5701da177e4SLinus Torvalds 571e6960e19SKrzysztof Helt static int __devinit snd_opti9xx_read_check(struct snd_opti9xx *chip) 572e6960e19SKrzysztof Helt { 573e6960e19SKrzysztof Helt unsigned char value; 574e6960e19SKrzysztof Helt #ifdef OPTi93X 575e6960e19SKrzysztof Helt unsigned long flags; 576e6960e19SKrzysztof Helt #endif 577e6960e19SKrzysztof Helt 578e6960e19SKrzysztof Helt chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, 579e6960e19SKrzysztof Helt "OPTi9xx MC"); 580e6960e19SKrzysztof Helt if (chip->res_mc_base == NULL) 581e6960e19SKrzysztof Helt return -EBUSY; 582e6960e19SKrzysztof Helt #ifndef OPTi93X 583e6960e19SKrzysztof Helt value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)); 584e6960e19SKrzysztof Helt if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1))) 585e6960e19SKrzysztof Helt if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1))) 586e6960e19SKrzysztof Helt return 0; 587e6960e19SKrzysztof Helt #else /* OPTi93X */ 588e6960e19SKrzysztof Helt chip->res_mc_indir = request_region(chip->mc_indir_index, 589e6960e19SKrzysztof Helt chip->mc_indir_size, 590e6960e19SKrzysztof Helt "OPTi93x MC"); 591e6960e19SKrzysztof Helt if (chip->res_mc_indir == NULL) 592e6960e19SKrzysztof Helt return -EBUSY; 593e6960e19SKrzysztof Helt 594e6960e19SKrzysztof Helt spin_lock_irqsave(&chip->lock, flags); 595e6960e19SKrzysztof Helt outb(chip->password, chip->mc_base + chip->pwd_reg); 596e6960e19SKrzysztof Helt outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base); 597e6960e19SKrzysztof Helt spin_unlock_irqrestore(&chip->lock, flags); 598e6960e19SKrzysztof Helt 599e6960e19SKrzysztof Helt value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)); 600e6960e19SKrzysztof Helt snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value); 601e6960e19SKrzysztof Helt if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value) 602e6960e19SKrzysztof Helt return 0; 603e6960e19SKrzysztof Helt 604e6960e19SKrzysztof Helt release_and_free_resource(chip->res_mc_indir); 605e6960e19SKrzysztof Helt chip->res_mc_indir = NULL; 606e6960e19SKrzysztof Helt #endif /* OPTi93X */ 607e6960e19SKrzysztof Helt release_and_free_resource(chip->res_mc_base); 608e6960e19SKrzysztof Helt chip->res_mc_base = NULL; 609e6960e19SKrzysztof Helt 610e6960e19SKrzysztof Helt return -ENODEV; 611e6960e19SKrzysztof Helt } 612e6960e19SKrzysztof Helt 6135e24c1c1STakashi Iwai static int __devinit snd_card_opti9xx_detect(struct snd_card *card, 6145e24c1c1STakashi Iwai struct snd_opti9xx *chip) 6151da177e4SLinus Torvalds { 6161da177e4SLinus Torvalds int i, err; 6171da177e4SLinus Torvalds 6181da177e4SLinus Torvalds #ifndef OPTi93X 6191da177e4SLinus Torvalds for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) { 620e6960e19SKrzysztof Helt #else 6211da177e4SLinus Torvalds for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) { 622e6960e19SKrzysztof Helt #endif 623e6960e19SKrzysztof Helt err = snd_opti9xx_init(chip, i); 624e6960e19SKrzysztof Helt if (err < 0) 6251da177e4SLinus Torvalds return err; 6261da177e4SLinus Torvalds 627e6960e19SKrzysztof Helt err = snd_opti9xx_read_check(chip); 628e6960e19SKrzysztof Helt if (err == 0) 6291da177e4SLinus Torvalds return 1; 630e6960e19SKrzysztof Helt #ifdef OPTi93X 631e6960e19SKrzysztof Helt chip->mc_indir_index = 0; 632e6960e19SKrzysztof Helt #endif 6331da177e4SLinus Torvalds } 6341da177e4SLinus Torvalds return -ENODEV; 6351da177e4SLinus Torvalds } 6361da177e4SLinus Torvalds 6371da177e4SLinus Torvalds #ifdef CONFIG_PNP 6385e24c1c1STakashi Iwai static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip, 6395e24c1c1STakashi Iwai struct pnp_card_link *card, 6401da177e4SLinus Torvalds const struct pnp_card_device_id *pid) 6411da177e4SLinus Torvalds { 6421da177e4SLinus Torvalds struct pnp_dev *pdev; 6431da177e4SLinus Torvalds int err; 6441da177e4SLinus Torvalds 6451da177e4SLinus Torvalds chip->dev = pnp_request_card_device(card, pid->devs[0].id, NULL); 646109c53f8SRene Herman if (chip->dev == NULL) 6471da177e4SLinus Torvalds return -EBUSY; 648109c53f8SRene Herman 6491da177e4SLinus Torvalds chip->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL); 6501da177e4SLinus Torvalds 6511da177e4SLinus Torvalds pdev = chip->dev; 6521da177e4SLinus Torvalds 6531da177e4SLinus Torvalds err = pnp_activate_dev(pdev); 6541da177e4SLinus Torvalds if (err < 0) { 6551da177e4SLinus Torvalds snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err); 6561da177e4SLinus Torvalds return err; 6571da177e4SLinus Torvalds } 6581da177e4SLinus Torvalds 6591da177e4SLinus Torvalds #ifdef OPTi93X 6601da177e4SLinus Torvalds port = pnp_port_start(pdev, 0) - 4; 6611ea73412STakashi Iwai fm_port = pnp_port_start(pdev, 1) + 8; 662e6960e19SKrzysztof Helt chip->mc_indir_index = pnp_port_start(pdev, 3) + 2; 663e6960e19SKrzysztof Helt chip->mc_indir_size = pnp_port_len(pdev, 3) - 2; 6641da177e4SLinus Torvalds #else 6651da177e4SLinus Torvalds if (pid->driver_data != 0x0924) 6661da177e4SLinus Torvalds port = pnp_port_start(pdev, 1); 6671ea73412STakashi Iwai fm_port = pnp_port_start(pdev, 2) + 8; 6681da177e4SLinus Torvalds #endif /* OPTi93X */ 6691da177e4SLinus Torvalds irq = pnp_irq(pdev, 0); 6701da177e4SLinus Torvalds dma1 = pnp_dma(pdev, 0); 6711da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X) 6721da177e4SLinus Torvalds dma2 = pnp_dma(pdev, 1); 6731da177e4SLinus Torvalds #endif /* CS4231 || OPTi93X */ 6741da177e4SLinus Torvalds 6751da177e4SLinus Torvalds pdev = chip->devmpu; 6761da177e4SLinus Torvalds if (pdev && mpu_port > 0) { 6771da177e4SLinus Torvalds err = pnp_activate_dev(pdev); 6781da177e4SLinus Torvalds if (err < 0) { 6791da177e4SLinus Torvalds snd_printk(KERN_ERR "AUDIO pnp configure failure\n"); 6801da177e4SLinus Torvalds mpu_port = -1; 6811da177e4SLinus Torvalds chip->devmpu = NULL; 6821da177e4SLinus Torvalds } else { 6831da177e4SLinus Torvalds mpu_port = pnp_port_start(pdev, 0); 6841da177e4SLinus Torvalds mpu_irq = pnp_irq(pdev, 0); 6851da177e4SLinus Torvalds } 6861da177e4SLinus Torvalds } 6871da177e4SLinus Torvalds return pid->driver_data; 6881da177e4SLinus Torvalds } 6891da177e4SLinus Torvalds #endif /* CONFIG_PNP */ 6901da177e4SLinus Torvalds 691346c7a68STakashi Iwai static void snd_card_opti9xx_free(struct snd_card *card) 6921da177e4SLinus Torvalds { 69399a0b768STakashi Iwai struct snd_opti9xx *chip = card->private_data; 6941da177e4SLinus Torvalds 6959f240a55SKrzysztof Helt if (chip) { 6969f240a55SKrzysztof Helt #ifdef OPTi93X 697*5f60e496SKrzysztof Helt if (chip->irq > 0) { 698*5f60e496SKrzysztof Helt disable_irq(chip->irq); 699*5f60e496SKrzysztof Helt free_irq(chip->irq, chip); 7009f240a55SKrzysztof Helt } 701e6960e19SKrzysztof Helt release_and_free_resource(chip->res_mc_indir); 7029f240a55SKrzysztof Helt #endif 703b1d5776dSTakashi Iwai release_and_free_resource(chip->res_mc_base); 7041da177e4SLinus Torvalds } 7059f240a55SKrzysztof Helt } 7061da177e4SLinus Torvalds 7075e24c1c1STakashi Iwai static int __devinit snd_opti9xx_probe(struct snd_card *card) 7081da177e4SLinus Torvalds { 7091da177e4SLinus Torvalds static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1}; 7101da177e4SLinus Torvalds int error; 711d8ea2393SKrzysztof Helt int xdma2; 71299a0b768STakashi Iwai struct snd_opti9xx *chip = card->private_data; 7137779f75fSKrzysztof Helt struct snd_wss *codec; 7149f240a55SKrzysztof Helt #ifdef CS4231 715346c7a68STakashi Iwai struct snd_timer *timer; 7169f240a55SKrzysztof Helt #endif 717346c7a68STakashi Iwai struct snd_pcm *pcm; 718346c7a68STakashi Iwai struct snd_rawmidi *rmidi; 719346c7a68STakashi Iwai struct snd_hwdep *synth; 7201da177e4SLinus Torvalds 7211da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X) 722d8ea2393SKrzysztof Helt xdma2 = dma2; 723a0d9274cSRene Herman #else 724d8ea2393SKrzysztof Helt xdma2 = -1; 7251da177e4SLinus Torvalds #endif 7261da177e4SLinus Torvalds 727d8ea2393SKrzysztof Helt if (port == SNDRV_AUTO_PORT) { 728d8ea2393SKrzysztof Helt port = snd_legacy_find_free_ioport(possible_ports, 4); 729d8ea2393SKrzysztof Helt if (port < 0) { 7304c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "unable to find a free WSS port\n"); 7311da177e4SLinus Torvalds return -EBUSY; 7321da177e4SLinus Torvalds } 7331da177e4SLinus Torvalds } 734d8ea2393SKrzysztof Helt error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2, 735d8ea2393SKrzysztof Helt mpu_port, mpu_irq); 7367779f75fSKrzysztof Helt if (error) 7371da177e4SLinus Torvalds return error; 7381da177e4SLinus Torvalds 739d8ea2393SKrzysztof Helt error = snd_wss_create(card, port + 4, -1, irq, dma1, xdma2, 740a0d9274cSRene Herman #ifdef OPTi93X 7417779f75fSKrzysztof Helt WSS_HW_OPTI93X, WSS_HWSHARE_IRQ, 742a0d9274cSRene Herman #else 743a0d9274cSRene Herman WSS_HW_DETECT, 0, 7449f240a55SKrzysztof Helt #endif 7457779f75fSKrzysztof Helt &codec); 7467779f75fSKrzysztof Helt if (error < 0) 7471da177e4SLinus Torvalds return error; 7489f240a55SKrzysztof Helt #ifdef OPTi93X 7499f240a55SKrzysztof Helt chip->codec = codec; 7509f240a55SKrzysztof Helt #endif 751ead893c0SKrzysztof Helt error = snd_wss_pcm(codec, 0, &pcm); 7525664daa1SKrzysztof Helt if (error < 0) 7535664daa1SKrzysztof Helt return error; 7547779f75fSKrzysztof Helt error = snd_wss_mixer(codec); 7557779f75fSKrzysztof Helt if (error < 0) 7561da177e4SLinus Torvalds return error; 7579f240a55SKrzysztof Helt #ifdef CS4231 7587779f75fSKrzysztof Helt error = snd_wss_timer(codec, 0, &timer); 7597779f75fSKrzysztof Helt if (error < 0) 7601da177e4SLinus Torvalds return error; 7615664daa1SKrzysztof Helt #endif 7625664daa1SKrzysztof Helt #ifdef OPTi93X 763d8ea2393SKrzysztof Helt error = request_irq(irq, snd_opti93x_interrupt, 764*5f60e496SKrzysztof Helt IRQF_DISABLED, DEV_NAME" - WSS", chip); 7659f240a55SKrzysztof Helt if (error < 0) { 766*5f60e496SKrzysztof Helt snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", irq); 7679f240a55SKrzysztof Helt return error; 7689f240a55SKrzysztof Helt } 7699f240a55SKrzysztof Helt #endif 770d8ea2393SKrzysztof Helt chip->irq = irq; 7711da177e4SLinus Torvalds strcpy(card->driver, chip->name); 7721da177e4SLinus Torvalds sprintf(card->shortname, "OPTi %s", card->driver); 7731da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X) 7741da177e4SLinus Torvalds sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d", 775d8ea2393SKrzysztof Helt card->shortname, pcm->name, port + 4, irq, dma1, xdma2); 7761da177e4SLinus Torvalds #else 7771da177e4SLinus Torvalds sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d", 778d8ea2393SKrzysztof Helt card->shortname, pcm->name, port + 4, irq, dma1); 7791da177e4SLinus Torvalds #endif /* CS4231 || OPTi93X */ 7801da177e4SLinus Torvalds 781d8ea2393SKrzysztof Helt if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT) 7821da177e4SLinus Torvalds rmidi = NULL; 783d8ea2393SKrzysztof Helt else { 784d8ea2393SKrzysztof Helt error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, 785d8ea2393SKrzysztof Helt mpu_port, 0, mpu_irq, IRQF_DISABLED, &rmidi); 786d8ea2393SKrzysztof Helt if (error) 78799a0b768STakashi Iwai snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", 788d8ea2393SKrzysztof Helt mpu_port); 789d8ea2393SKrzysztof Helt } 7901da177e4SLinus Torvalds 791d8ea2393SKrzysztof Helt if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) { 792346c7a68STakashi Iwai struct snd_opl3 *opl3 = NULL; 7931da177e4SLinus Torvalds #ifndef OPTi93X 7941da177e4SLinus Torvalds if (chip->hardware == OPTi9XX_HW_82C928 || 7951da177e4SLinus Torvalds chip->hardware == OPTi9XX_HW_82C929 || 7961da177e4SLinus Torvalds chip->hardware == OPTi9XX_HW_82C924) { 797346c7a68STakashi Iwai struct snd_opl4 *opl4; 7981da177e4SLinus Torvalds /* assume we have an OPL4 */ 7991da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 8001da177e4SLinus Torvalds 0x20, 0x20); 801d8ea2393SKrzysztof Helt if (snd_opl4_create(card, fm_port, fm_port - 8, 8021da177e4SLinus Torvalds 2, &opl3, &opl4) < 0) { 8031da177e4SLinus Torvalds /* no luck, use OPL3 instead */ 8041da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 8051da177e4SLinus Torvalds 0x00, 0x20); 8061da177e4SLinus Torvalds } 8071da177e4SLinus Torvalds } 8081da177e4SLinus Torvalds #endif /* !OPTi93X */ 809d8ea2393SKrzysztof Helt if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2, 8101da177e4SLinus Torvalds OPL3_HW_AUTO, 0, &opl3) < 0) { 81199a0b768STakashi Iwai snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n", 812d8ea2393SKrzysztof Helt fm_port, fm_port + 4 - 1); 8131da177e4SLinus Torvalds } 8141da177e4SLinus Torvalds if (opl3) { 815aa9c293aSKrzysztof Helt error = snd_opl3_hwdep_new(opl3, 0, 1, &synth); 816aa9c293aSKrzysztof Helt if (error < 0) 8171da177e4SLinus Torvalds return error; 8181da177e4SLinus Torvalds } 8191da177e4SLinus Torvalds } 8201da177e4SLinus Torvalds 82199a0b768STakashi Iwai return snd_card_register(card); 82299a0b768STakashi Iwai } 82399a0b768STakashi Iwai 8243e7fb9f7STakashi Iwai static int snd_opti9xx_card_new(struct snd_card **cardp) 82599a0b768STakashi Iwai { 82699a0b768STakashi Iwai struct snd_card *card; 827b1a0aac0STakashi Iwai int err; 82899a0b768STakashi Iwai 829c95eadd2STakashi Iwai err = snd_card_create(index, id, THIS_MODULE, 830c95eadd2STakashi Iwai sizeof(struct snd_opti9xx), &card); 831c95eadd2STakashi Iwai if (err < 0) 8323e7fb9f7STakashi Iwai return err; 83399a0b768STakashi Iwai card->private_free = snd_card_opti9xx_free; 8343e7fb9f7STakashi Iwai *cardp = card; 8353e7fb9f7STakashi Iwai return 0; 83699a0b768STakashi Iwai } 83799a0b768STakashi Iwai 8385e24c1c1STakashi Iwai static int __devinit snd_opti9xx_isa_match(struct device *devptr, 8395e24c1c1STakashi Iwai unsigned int dev) 8405e24c1c1STakashi Iwai { 841101f6f4bSTakashi Iwai #ifdef CONFIG_PNP 8425e24c1c1STakashi Iwai if (snd_opti9xx_pnp_is_probed) 8435e24c1c1STakashi Iwai return 0; 8445e24c1c1STakashi Iwai if (isapnp) 8455e24c1c1STakashi Iwai return 0; 846101f6f4bSTakashi Iwai #endif 8475e24c1c1STakashi Iwai return 1; 8485e24c1c1STakashi Iwai } 8495e24c1c1STakashi Iwai 8505e24c1c1STakashi Iwai static int __devinit snd_opti9xx_isa_probe(struct device *devptr, 8515e24c1c1STakashi Iwai unsigned int dev) 85299a0b768STakashi Iwai { 85399a0b768STakashi Iwai struct snd_card *card; 85499a0b768STakashi Iwai int error; 85599a0b768STakashi Iwai static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1}; 85699a0b768STakashi Iwai #ifdef OPTi93X 85799a0b768STakashi Iwai static int possible_irqs[] = {5, 9, 10, 11, 7, -1}; 85899a0b768STakashi Iwai #else 85999a0b768STakashi Iwai static int possible_irqs[] = {9, 10, 11, 7, -1}; 86099a0b768STakashi Iwai #endif /* OPTi93X */ 86199a0b768STakashi Iwai static int possible_mpu_irqs[] = {5, 9, 10, 7, -1}; 86299a0b768STakashi Iwai static int possible_dma1s[] = {3, 1, 0, -1}; 86399a0b768STakashi Iwai #if defined(CS4231) || defined(OPTi93X) 86499a0b768STakashi Iwai static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}}; 86599a0b768STakashi Iwai #endif /* CS4231 || OPTi93X */ 86699a0b768STakashi Iwai 86799a0b768STakashi Iwai if (mpu_port == SNDRV_AUTO_PORT) { 86899a0b768STakashi Iwai if ((mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) { 86999a0b768STakashi Iwai snd_printk(KERN_ERR "unable to find a free MPU401 port\n"); 87099a0b768STakashi Iwai return -EBUSY; 87199a0b768STakashi Iwai } 87299a0b768STakashi Iwai } 87399a0b768STakashi Iwai if (irq == SNDRV_AUTO_IRQ) { 87499a0b768STakashi Iwai if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { 87599a0b768STakashi Iwai snd_printk(KERN_ERR "unable to find a free IRQ\n"); 87699a0b768STakashi Iwai return -EBUSY; 87799a0b768STakashi Iwai } 87899a0b768STakashi Iwai } 87999a0b768STakashi Iwai if (mpu_irq == SNDRV_AUTO_IRQ) { 88099a0b768STakashi Iwai if ((mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) { 88199a0b768STakashi Iwai snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n"); 88299a0b768STakashi Iwai return -EBUSY; 88399a0b768STakashi Iwai } 88499a0b768STakashi Iwai } 88599a0b768STakashi Iwai if (dma1 == SNDRV_AUTO_DMA) { 88699a0b768STakashi Iwai if ((dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) { 88799a0b768STakashi Iwai snd_printk(KERN_ERR "unable to find a free DMA1\n"); 88899a0b768STakashi Iwai return -EBUSY; 88999a0b768STakashi Iwai } 89099a0b768STakashi Iwai } 89199a0b768STakashi Iwai #if defined(CS4231) || defined(OPTi93X) 89299a0b768STakashi Iwai if (dma2 == SNDRV_AUTO_DMA) { 89399a0b768STakashi Iwai if ((dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4])) < 0) { 8944c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "unable to find a free DMA2\n"); 89599a0b768STakashi Iwai return -EBUSY; 89699a0b768STakashi Iwai } 89799a0b768STakashi Iwai } 89899a0b768STakashi Iwai #endif 89999a0b768STakashi Iwai 9003e7fb9f7STakashi Iwai error = snd_opti9xx_card_new(&card); 9013e7fb9f7STakashi Iwai if (error < 0) 9023e7fb9f7STakashi Iwai return error; 90399a0b768STakashi Iwai 90499a0b768STakashi Iwai if ((error = snd_card_opti9xx_detect(card, card->private_data)) < 0) { 9051da177e4SLinus Torvalds snd_card_free(card); 9061da177e4SLinus Torvalds return error; 9071da177e4SLinus Torvalds } 9085e24c1c1STakashi Iwai snd_card_set_dev(card, devptr); 90999a0b768STakashi Iwai if ((error = snd_opti9xx_probe(card)) < 0) { 91099a0b768STakashi Iwai snd_card_free(card); 91199a0b768STakashi Iwai return error; 91299a0b768STakashi Iwai } 9135e24c1c1STakashi Iwai dev_set_drvdata(devptr, card); 9141da177e4SLinus Torvalds return 0; 9151da177e4SLinus Torvalds } 9161da177e4SLinus Torvalds 9175e24c1c1STakashi Iwai static int __devexit snd_opti9xx_isa_remove(struct device *devptr, 9185e24c1c1STakashi Iwai unsigned int dev) 91999a0b768STakashi Iwai { 9205e24c1c1STakashi Iwai snd_card_free(dev_get_drvdata(devptr)); 9215e24c1c1STakashi Iwai dev_set_drvdata(devptr, NULL); 92299a0b768STakashi Iwai return 0; 92399a0b768STakashi Iwai } 92499a0b768STakashi Iwai 9255e24c1c1STakashi Iwai static struct isa_driver snd_opti9xx_driver = { 9265e24c1c1STakashi Iwai .match = snd_opti9xx_isa_match, 9275e24c1c1STakashi Iwai .probe = snd_opti9xx_isa_probe, 9285e24c1c1STakashi Iwai .remove = __devexit_p(snd_opti9xx_isa_remove), 92999a0b768STakashi Iwai /* FIXME: suspend/resume */ 93099a0b768STakashi Iwai .driver = { 93183c51c0aSRene Herman .name = DEV_NAME 93299a0b768STakashi Iwai }, 93399a0b768STakashi Iwai }; 93499a0b768STakashi Iwai 9351da177e4SLinus Torvalds #ifdef CONFIG_PNP 9365e24c1c1STakashi Iwai static int __devinit snd_opti9xx_pnp_probe(struct pnp_card_link *pcard, 93799a0b768STakashi Iwai const struct pnp_card_device_id *pid) 93899a0b768STakashi Iwai { 93999a0b768STakashi Iwai struct snd_card *card; 94099a0b768STakashi Iwai int error, hw; 94199a0b768STakashi Iwai struct snd_opti9xx *chip; 94299a0b768STakashi Iwai 94399a0b768STakashi Iwai if (snd_opti9xx_pnp_is_probed) 94499a0b768STakashi Iwai return -EBUSY; 94599a0b768STakashi Iwai if (! isapnp) 94699a0b768STakashi Iwai return -ENODEV; 9473e7fb9f7STakashi Iwai error = snd_opti9xx_card_new(&card); 9483e7fb9f7STakashi Iwai if (error < 0) 9493e7fb9f7STakashi Iwai return error; 95099a0b768STakashi Iwai chip = card->private_data; 95199a0b768STakashi Iwai 95299a0b768STakashi Iwai hw = snd_card_opti9xx_pnp(chip, pcard, pid); 95399a0b768STakashi Iwai switch (hw) { 95499a0b768STakashi Iwai case 0x0924: 95599a0b768STakashi Iwai hw = OPTi9XX_HW_82C924; 95699a0b768STakashi Iwai break; 95799a0b768STakashi Iwai case 0x0925: 95899a0b768STakashi Iwai hw = OPTi9XX_HW_82C925; 95999a0b768STakashi Iwai break; 96099a0b768STakashi Iwai case 0x0931: 96199a0b768STakashi Iwai hw = OPTi9XX_HW_82C931; 96299a0b768STakashi Iwai break; 96399a0b768STakashi Iwai default: 96499a0b768STakashi Iwai snd_card_free(card); 96599a0b768STakashi Iwai return -ENODEV; 96699a0b768STakashi Iwai } 96799a0b768STakashi Iwai 96899a0b768STakashi Iwai if ((error = snd_opti9xx_init(chip, hw))) { 96999a0b768STakashi Iwai snd_card_free(card); 97099a0b768STakashi Iwai return error; 97199a0b768STakashi Iwai } 97299a0b768STakashi Iwai if (hw <= OPTi9XX_HW_82C930) 97399a0b768STakashi Iwai chip->mc_base -= 0x80; 974e6960e19SKrzysztof Helt 975e6960e19SKrzysztof Helt error = snd_opti9xx_read_check(chip); 976e6960e19SKrzysztof Helt if (error) { 977e6960e19SKrzysztof Helt snd_printk(KERN_ERR "OPTI chip not found\n"); 978e6960e19SKrzysztof Helt snd_card_free(card); 979e6960e19SKrzysztof Helt return error; 980e6960e19SKrzysztof Helt } 98199a0b768STakashi Iwai snd_card_set_dev(card, &pcard->card->dev); 98299a0b768STakashi Iwai if ((error = snd_opti9xx_probe(card)) < 0) { 98399a0b768STakashi Iwai snd_card_free(card); 98499a0b768STakashi Iwai return error; 98599a0b768STakashi Iwai } 98699a0b768STakashi Iwai pnp_set_card_drvdata(pcard, card); 98799a0b768STakashi Iwai snd_opti9xx_pnp_is_probed = 1; 98899a0b768STakashi Iwai return 0; 98999a0b768STakashi Iwai } 99099a0b768STakashi Iwai 9911da177e4SLinus Torvalds static void __devexit snd_opti9xx_pnp_remove(struct pnp_card_link * pcard) 9921da177e4SLinus Torvalds { 99399a0b768STakashi Iwai snd_card_free(pnp_get_card_drvdata(pcard)); 99499a0b768STakashi Iwai pnp_set_card_drvdata(pcard, NULL); 99599a0b768STakashi Iwai snd_opti9xx_pnp_is_probed = 0; 9961da177e4SLinus Torvalds } 9971da177e4SLinus Torvalds 9981da177e4SLinus Torvalds static struct pnp_card_driver opti9xx_pnpc_driver = { 9991da177e4SLinus Torvalds .flags = PNP_DRIVER_RES_DISABLE, 10001da177e4SLinus Torvalds .name = "opti9xx", 10011da177e4SLinus Torvalds .id_table = snd_opti9xx_pnpids, 100299a0b768STakashi Iwai .probe = snd_opti9xx_pnp_probe, 10031da177e4SLinus Torvalds .remove = __devexit_p(snd_opti9xx_pnp_remove), 10041da177e4SLinus Torvalds }; 10051da177e4SLinus Torvalds #endif 10061da177e4SLinus Torvalds 100799a0b768STakashi Iwai #ifdef OPTi93X 100899a0b768STakashi Iwai #define CHIP_NAME "82C93x" 100999a0b768STakashi Iwai #else 101099a0b768STakashi Iwai #define CHIP_NAME "82C92x" 101199a0b768STakashi Iwai #endif 101299a0b768STakashi Iwai 10131da177e4SLinus Torvalds static int __init alsa_card_opti9xx_init(void) 10141da177e4SLinus Torvalds { 10150bbbc4caSTakashi Iwai #ifdef CONFIG_PNP 101699a0b768STakashi Iwai pnp_register_card_driver(&opti9xx_pnpc_driver); 101799a0b768STakashi Iwai if (snd_opti9xx_pnp_is_probed) 10181da177e4SLinus Torvalds return 0; 1019101f6f4bSTakashi Iwai pnp_unregister_card_driver(&opti9xx_pnpc_driver); 10200bbbc4caSTakashi Iwai #endif 10215e24c1c1STakashi Iwai return isa_register_driver(&snd_opti9xx_driver, 1); 10221da177e4SLinus Torvalds } 10231da177e4SLinus Torvalds 10241da177e4SLinus Torvalds static void __exit alsa_card_opti9xx_exit(void) 10251da177e4SLinus Torvalds { 1026f7a9275dSClemens Ladisch if (!snd_opti9xx_pnp_is_probed) { 10275e24c1c1STakashi Iwai isa_unregister_driver(&snd_opti9xx_driver); 10285e24c1c1STakashi Iwai return; 1029f7a9275dSClemens Ladisch } 10300bbbc4caSTakashi Iwai #ifdef CONFIG_PNP 10311da177e4SLinus Torvalds pnp_unregister_card_driver(&opti9xx_pnpc_driver); 10320bbbc4caSTakashi Iwai #endif 10331da177e4SLinus Torvalds } 10341da177e4SLinus Torvalds 10351da177e4SLinus Torvalds module_init(alsa_card_opti9xx_init) 10361da177e4SLinus Torvalds module_exit(alsa_card_opti9xx_exit) 1037