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; 1387779f75fSKrzysztof Helt struct snd_wss *codec; 1391da177e4SLinus Torvalds #endif /* OPTi93X */ 1401da177e4SLinus Torvalds unsigned long pwd_reg; 1411da177e4SLinus Torvalds 1421da177e4SLinus Torvalds spinlock_t lock; 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds long wss_base; 1451da177e4SLinus Torvalds int irq; 1461da177e4SLinus Torvalds int dma1; 1471da177e4SLinus Torvalds int dma2; 1481da177e4SLinus Torvalds 1491da177e4SLinus Torvalds long fm_port; 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds long mpu_port; 1521da177e4SLinus Torvalds int mpu_irq; 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds #ifdef CONFIG_PNP 1551da177e4SLinus Torvalds struct pnp_dev *dev; 1561da177e4SLinus Torvalds struct pnp_dev *devmpu; 1571da177e4SLinus Torvalds #endif /* CONFIG_PNP */ 1581da177e4SLinus Torvalds }; 1591da177e4SLinus Torvalds 16099a0b768STakashi Iwai static int snd_opti9xx_pnp_is_probed; 1611da177e4SLinus Torvalds 1621da177e4SLinus Torvalds #ifdef CONFIG_PNP 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds static struct pnp_card_device_id snd_opti9xx_pnpids[] = { 1651da177e4SLinus Torvalds #ifndef OPTi93X 1661da177e4SLinus Torvalds /* OPTi 82C924 */ 1671da177e4SLinus Torvalds { .id = "OPT0924", .devs = { { "OPT0000" }, { "OPT0002" } }, .driver_data = 0x0924 }, 1681da177e4SLinus Torvalds /* OPTi 82C925 */ 1691da177e4SLinus Torvalds { .id = "OPT0925", .devs = { { "OPT9250" }, { "OPT0002" } }, .driver_data = 0x0925 }, 1701da177e4SLinus Torvalds #else 1711da177e4SLinus Torvalds /* OPTi 82C931/3 */ 1721da177e4SLinus Torvalds { .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } }, .driver_data = 0x0931 }, 1731da177e4SLinus Torvalds #endif /* OPTi93X */ 1741da177e4SLinus Torvalds { .id = "" } 1751da177e4SLinus Torvalds }; 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids); 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds #endif /* CONFIG_PNP */ 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds #ifdef OPTi93X 18283c51c0aSRene Herman #define DEV_NAME "opti93x" 1831da177e4SLinus Torvalds #else 18483c51c0aSRene Herman #define DEV_NAME "opti92x" 18583c51c0aSRene Herman #endif 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds static char * snd_opti9xx_names[] = { 1881da177e4SLinus Torvalds "unkown", 1891da177e4SLinus Torvalds "82C928", "82C929", 1901da177e4SLinus Torvalds "82C924", "82C925", 1911da177e4SLinus Torvalds "82C930", "82C931", "82C933" 1921da177e4SLinus Torvalds }; 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds 1955e24c1c1STakashi Iwai static long __devinit snd_legacy_find_free_ioport(long *port_table, long size) 1961da177e4SLinus Torvalds { 1971da177e4SLinus Torvalds while (*port_table != -1) { 198b1d5776dSTakashi Iwai if (request_region(*port_table, size, "ALSA test")) { 199b1d5776dSTakashi Iwai release_region(*port_table, size); 2001da177e4SLinus Torvalds return *port_table; 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds port_table++; 2031da177e4SLinus Torvalds } 2041da177e4SLinus Torvalds return -1; 2051da177e4SLinus Torvalds } 2061da177e4SLinus Torvalds 2075e24c1c1STakashi Iwai static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip, 2085e24c1c1STakashi Iwai unsigned short hardware) 2091da177e4SLinus Torvalds { 2101da177e4SLinus Torvalds static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds chip->hardware = hardware; 2131da177e4SLinus Torvalds strcpy(chip->name, snd_opti9xx_names[hardware]); 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds chip->mc_base_size = opti9xx_mc_size[hardware]; 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds spin_lock_init(&chip->lock); 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds chip->wss_base = -1; 2201da177e4SLinus Torvalds chip->irq = -1; 2211da177e4SLinus Torvalds chip->dma1 = -1; 2221da177e4SLinus Torvalds chip->dma2 = -1; 2231da177e4SLinus Torvalds chip->fm_port = -1; 2241da177e4SLinus Torvalds chip->mpu_port = -1; 2251da177e4SLinus Torvalds chip->mpu_irq = -1; 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds switch (hardware) { 2281da177e4SLinus Torvalds #ifndef OPTi93X 2291da177e4SLinus Torvalds case OPTi9XX_HW_82C928: 2301da177e4SLinus Torvalds case OPTi9XX_HW_82C929: 2311da177e4SLinus Torvalds chip->mc_base = 0xf8c; 2321da177e4SLinus Torvalds chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3; 2331da177e4SLinus Torvalds chip->pwd_reg = 3; 2341da177e4SLinus Torvalds break; 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds case OPTi9XX_HW_82C924: 2371da177e4SLinus Torvalds case OPTi9XX_HW_82C925: 2381da177e4SLinus Torvalds chip->mc_base = 0xf8c; 2391da177e4SLinus Torvalds chip->password = 0xe5; 2401da177e4SLinus Torvalds chip->pwd_reg = 3; 2411da177e4SLinus Torvalds break; 2421da177e4SLinus Torvalds #else /* OPTi93X */ 2431da177e4SLinus Torvalds 2441da177e4SLinus Torvalds case OPTi9XX_HW_82C930: 2451da177e4SLinus Torvalds case OPTi9XX_HW_82C931: 2461da177e4SLinus Torvalds case OPTi9XX_HW_82C933: 2471da177e4SLinus Torvalds chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d; 2481da177e4SLinus Torvalds chip->mc_indir_index = 0xe0e; 2491da177e4SLinus Torvalds chip->password = 0xe4; 2501da177e4SLinus Torvalds chip->pwd_reg = 0; 2511da177e4SLinus Torvalds break; 2521da177e4SLinus Torvalds #endif /* OPTi93X */ 2531da177e4SLinus Torvalds 2541da177e4SLinus Torvalds default: 255*4c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "chip %d not supported\n", hardware); 2561da177e4SLinus Torvalds return -ENODEV; 2571da177e4SLinus Torvalds } 2581da177e4SLinus Torvalds return 0; 2591da177e4SLinus Torvalds } 2601da177e4SLinus Torvalds 261346c7a68STakashi Iwai static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip, 2621da177e4SLinus Torvalds unsigned char reg) 2631da177e4SLinus Torvalds { 2641da177e4SLinus Torvalds unsigned long flags; 2651da177e4SLinus Torvalds unsigned char retval = 0xff; 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds spin_lock_irqsave(&chip->lock, flags); 2681da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds switch (chip->hardware) { 2711da177e4SLinus Torvalds #ifndef OPTi93X 2721da177e4SLinus Torvalds case OPTi9XX_HW_82C924: 2731da177e4SLinus Torvalds case OPTi9XX_HW_82C925: 2741da177e4SLinus Torvalds if (reg > 7) { 2751da177e4SLinus Torvalds outb(reg, chip->mc_base + 8); 2761da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 2771da177e4SLinus Torvalds retval = inb(chip->mc_base + 9); 2781da177e4SLinus Torvalds break; 2791da177e4SLinus Torvalds } 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds case OPTi9XX_HW_82C928: 2821da177e4SLinus Torvalds case OPTi9XX_HW_82C929: 2831da177e4SLinus Torvalds retval = inb(chip->mc_base + reg); 2841da177e4SLinus Torvalds break; 2851da177e4SLinus Torvalds #else /* OPTi93X */ 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds case OPTi9XX_HW_82C930: 2881da177e4SLinus Torvalds case OPTi9XX_HW_82C931: 2891da177e4SLinus Torvalds case OPTi9XX_HW_82C933: 2901da177e4SLinus Torvalds outb(reg, chip->mc_indir_index); 2911da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 2921da177e4SLinus Torvalds retval = inb(chip->mc_indir_index + 1); 2931da177e4SLinus Torvalds break; 2941da177e4SLinus Torvalds #endif /* OPTi93X */ 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds default: 297*4c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->lock, flags); 3011da177e4SLinus Torvalds return retval; 3021da177e4SLinus Torvalds } 3031da177e4SLinus Torvalds 304346c7a68STakashi Iwai static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg, 3051da177e4SLinus Torvalds unsigned char value) 3061da177e4SLinus Torvalds { 3071da177e4SLinus Torvalds unsigned long flags; 3081da177e4SLinus Torvalds 3091da177e4SLinus Torvalds spin_lock_irqsave(&chip->lock, flags); 3101da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 3111da177e4SLinus Torvalds 3121da177e4SLinus Torvalds switch (chip->hardware) { 3131da177e4SLinus Torvalds #ifndef OPTi93X 3141da177e4SLinus Torvalds case OPTi9XX_HW_82C924: 3151da177e4SLinus Torvalds case OPTi9XX_HW_82C925: 3161da177e4SLinus Torvalds if (reg > 7) { 3171da177e4SLinus Torvalds outb(reg, chip->mc_base + 8); 3181da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 3191da177e4SLinus Torvalds outb(value, chip->mc_base + 9); 3201da177e4SLinus Torvalds break; 3211da177e4SLinus Torvalds } 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds case OPTi9XX_HW_82C928: 3241da177e4SLinus Torvalds case OPTi9XX_HW_82C929: 3251da177e4SLinus Torvalds outb(value, chip->mc_base + reg); 3261da177e4SLinus Torvalds break; 3271da177e4SLinus Torvalds #else /* OPTi93X */ 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds case OPTi9XX_HW_82C930: 3301da177e4SLinus Torvalds case OPTi9XX_HW_82C931: 3311da177e4SLinus Torvalds case OPTi9XX_HW_82C933: 3321da177e4SLinus Torvalds outb(reg, chip->mc_indir_index); 3331da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 3341da177e4SLinus Torvalds outb(value, chip->mc_indir_index + 1); 3351da177e4SLinus Torvalds break; 3361da177e4SLinus Torvalds #endif /* OPTi93X */ 3371da177e4SLinus Torvalds 3381da177e4SLinus Torvalds default: 339*4c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 3401da177e4SLinus Torvalds } 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->lock, flags); 3431da177e4SLinus Torvalds } 3441da177e4SLinus Torvalds 3451da177e4SLinus Torvalds 3461da177e4SLinus Torvalds #define snd_opti9xx_write_mask(chip, reg, value, mask) \ 3471da177e4SLinus Torvalds snd_opti9xx_write(chip, reg, \ 3481da177e4SLinus Torvalds (snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask))) 3491da177e4SLinus Torvalds 3501da177e4SLinus Torvalds 3515e24c1c1STakashi Iwai static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip) 3521da177e4SLinus Torvalds { 3531da177e4SLinus Torvalds unsigned char wss_base_bits; 3541da177e4SLinus Torvalds unsigned char irq_bits; 3551da177e4SLinus Torvalds unsigned char dma_bits; 3561da177e4SLinus Torvalds unsigned char mpu_port_bits = 0; 3571da177e4SLinus Torvalds unsigned char mpu_irq_bits; 3581da177e4SLinus Torvalds 3591da177e4SLinus Torvalds switch (chip->hardware) { 3601da177e4SLinus Torvalds #ifndef OPTi93X 3611da177e4SLinus Torvalds case OPTi9XX_HW_82C924: 3621da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc); 3631da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02); 3641da177e4SLinus Torvalds 3651da177e4SLinus Torvalds case OPTi9XX_HW_82C925: 3661da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); 3671da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); 3681da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff); 3691da177e4SLinus Torvalds #ifdef CS4231 3701da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); 3711da177e4SLinus Torvalds #else 3721da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); 3731da177e4SLinus Torvalds #endif /* CS4231 */ 3741da177e4SLinus Torvalds break; 3751da177e4SLinus Torvalds 3761da177e4SLinus Torvalds case OPTi9XX_HW_82C928: 3771da177e4SLinus Torvalds case OPTi9XX_HW_82C929: 3781da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); 3791da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); 3801da177e4SLinus Torvalds /* 3811da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae); 3821da177e4SLinus Torvalds */ 3831da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c); 3841da177e4SLinus Torvalds #ifdef CS4231 3851da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); 3861da177e4SLinus Torvalds #else 3871da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); 3881da177e4SLinus Torvalds #endif /* CS4231 */ 3891da177e4SLinus Torvalds break; 3901da177e4SLinus Torvalds 3911da177e4SLinus Torvalds #else /* OPTi93X */ 3921da177e4SLinus Torvalds case OPTi9XX_HW_82C931: 3931da177e4SLinus Torvalds case OPTi9XX_HW_82C933: 394f81b953dSKrzysztof Helt /* 395f81b953dSKrzysztof Helt * The BTC 1817DW has QS1000 wavetable which is connected 396f81b953dSKrzysztof Helt * to the serial digital input of the OPTI931. 397f81b953dSKrzysztof Helt */ 398f81b953dSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(21), 0x82, 0xff); 399f81b953dSKrzysztof Helt /* 400f81b953dSKrzysztof Helt * This bit sets OPTI931 to automaticaly select FM 401f81b953dSKrzysztof Helt * or digital input signal. 402f81b953dSKrzysztof Helt */ 403f81b953dSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01); 4043ae5f36aSKrzysztof Helt case OPTi9XX_HW_82C930: /* FALL THROUGH */ 4053ae5f36aSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03); 4063ae5f36aSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff); 4073ae5f36aSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 | 4083ae5f36aSKrzysztof Helt (chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04), 4093ae5f36aSKrzysztof Helt 0x34); 4103ae5f36aSKrzysztof Helt snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf); 4111da177e4SLinus Torvalds break; 4121da177e4SLinus Torvalds #endif /* OPTi93X */ 4131da177e4SLinus Torvalds 4141da177e4SLinus Torvalds default: 415*4c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 4161da177e4SLinus Torvalds return -EINVAL; 4171da177e4SLinus Torvalds } 4181da177e4SLinus Torvalds 4191da177e4SLinus Torvalds switch (chip->wss_base) { 4201da177e4SLinus Torvalds case 0x530: 4211da177e4SLinus Torvalds wss_base_bits = 0x00; 4221da177e4SLinus Torvalds break; 4231da177e4SLinus Torvalds case 0x604: 4241da177e4SLinus Torvalds wss_base_bits = 0x03; 4251da177e4SLinus Torvalds break; 4261da177e4SLinus Torvalds case 0xe80: 4271da177e4SLinus Torvalds wss_base_bits = 0x01; 4281da177e4SLinus Torvalds break; 4291da177e4SLinus Torvalds case 0xf40: 4301da177e4SLinus Torvalds wss_base_bits = 0x02; 4311da177e4SLinus Torvalds break; 4321da177e4SLinus Torvalds default: 433*4c9f1d3eSTakashi Iwai snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", 434*4c9f1d3eSTakashi Iwai chip->wss_base); 4351da177e4SLinus Torvalds goto __skip_base; 4361da177e4SLinus Torvalds } 4371da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30); 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds __skip_base: 4401da177e4SLinus Torvalds switch (chip->irq) { 4411da177e4SLinus Torvalds //#ifdef OPTi93X 4421da177e4SLinus Torvalds case 5: 4431da177e4SLinus Torvalds irq_bits = 0x05; 4441da177e4SLinus Torvalds break; 4451da177e4SLinus Torvalds //#endif /* OPTi93X */ 4461da177e4SLinus Torvalds case 7: 4471da177e4SLinus Torvalds irq_bits = 0x01; 4481da177e4SLinus Torvalds break; 4491da177e4SLinus Torvalds case 9: 4501da177e4SLinus Torvalds irq_bits = 0x02; 4511da177e4SLinus Torvalds break; 4521da177e4SLinus Torvalds case 10: 4531da177e4SLinus Torvalds irq_bits = 0x03; 4541da177e4SLinus Torvalds break; 4551da177e4SLinus Torvalds case 11: 4561da177e4SLinus Torvalds irq_bits = 0x04; 4571da177e4SLinus Torvalds break; 4581da177e4SLinus Torvalds default: 459*4c9f1d3eSTakashi Iwai snd_printk(KERN_WARNING "WSS irq # %d not valid\n", chip->irq); 4601da177e4SLinus Torvalds goto __skip_resources; 4611da177e4SLinus Torvalds } 4621da177e4SLinus Torvalds 4631da177e4SLinus Torvalds switch (chip->dma1) { 4641da177e4SLinus Torvalds case 0: 4651da177e4SLinus Torvalds dma_bits = 0x01; 4661da177e4SLinus Torvalds break; 4671da177e4SLinus Torvalds case 1: 4681da177e4SLinus Torvalds dma_bits = 0x02; 4691da177e4SLinus Torvalds break; 4701da177e4SLinus Torvalds case 3: 4711da177e4SLinus Torvalds dma_bits = 0x03; 4721da177e4SLinus Torvalds break; 4731da177e4SLinus Torvalds default: 474*4c9f1d3eSTakashi Iwai snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n", 475*4c9f1d3eSTakashi Iwai chip->dma1); 4761da177e4SLinus Torvalds goto __skip_resources; 4771da177e4SLinus Torvalds } 4781da177e4SLinus Torvalds 4791da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X) 4801da177e4SLinus Torvalds if (chip->dma1 == chip->dma2) { 481*4c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "don't want to share dmas\n"); 4821da177e4SLinus Torvalds return -EBUSY; 4831da177e4SLinus Torvalds } 4841da177e4SLinus Torvalds 4851da177e4SLinus Torvalds switch (chip->dma2) { 4861da177e4SLinus Torvalds case 0: 4871da177e4SLinus Torvalds case 1: 4881da177e4SLinus Torvalds break; 4891da177e4SLinus Torvalds default: 490*4c9f1d3eSTakashi Iwai snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n", 491*4c9f1d3eSTakashi Iwai chip->dma2); 4921da177e4SLinus Torvalds goto __skip_resources; 4931da177e4SLinus Torvalds } 4941da177e4SLinus Torvalds dma_bits |= 0x04; 4951da177e4SLinus Torvalds #endif /* CS4231 || OPTi93X */ 4961da177e4SLinus Torvalds 4971da177e4SLinus Torvalds #ifndef OPTi93X 4981da177e4SLinus Torvalds outb(irq_bits << 3 | dma_bits, chip->wss_base); 4991da177e4SLinus Torvalds #else /* OPTi93X */ 5001da177e4SLinus Torvalds snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits)); 5011da177e4SLinus Torvalds #endif /* OPTi93X */ 5021da177e4SLinus Torvalds 5031da177e4SLinus Torvalds __skip_resources: 5041da177e4SLinus Torvalds if (chip->hardware > OPTi9XX_HW_82C928) { 5051da177e4SLinus Torvalds switch (chip->mpu_port) { 5061da177e4SLinus Torvalds case 0: 5071da177e4SLinus Torvalds case -1: 5081da177e4SLinus Torvalds break; 5091da177e4SLinus Torvalds case 0x300: 5101da177e4SLinus Torvalds mpu_port_bits = 0x03; 5111da177e4SLinus Torvalds break; 5121da177e4SLinus Torvalds case 0x310: 5131da177e4SLinus Torvalds mpu_port_bits = 0x02; 5141da177e4SLinus Torvalds break; 5151da177e4SLinus Torvalds case 0x320: 5161da177e4SLinus Torvalds mpu_port_bits = 0x01; 5171da177e4SLinus Torvalds break; 5181da177e4SLinus Torvalds case 0x330: 5191da177e4SLinus Torvalds mpu_port_bits = 0x00; 5201da177e4SLinus Torvalds break; 5211da177e4SLinus Torvalds default: 522*4c9f1d3eSTakashi Iwai snd_printk(KERN_WARNING 523*4c9f1d3eSTakashi Iwai "MPU-401 port 0x%lx not valid\n", 5241da177e4SLinus Torvalds chip->mpu_port); 5251da177e4SLinus Torvalds goto __skip_mpu; 5261da177e4SLinus Torvalds } 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds switch (chip->mpu_irq) { 5291da177e4SLinus Torvalds case 5: 5301da177e4SLinus Torvalds mpu_irq_bits = 0x02; 5311da177e4SLinus Torvalds break; 5321da177e4SLinus Torvalds case 7: 5331da177e4SLinus Torvalds mpu_irq_bits = 0x03; 5341da177e4SLinus Torvalds break; 5351da177e4SLinus Torvalds case 9: 5361da177e4SLinus Torvalds mpu_irq_bits = 0x00; 5371da177e4SLinus Torvalds break; 5381da177e4SLinus Torvalds case 10: 5391da177e4SLinus Torvalds mpu_irq_bits = 0x01; 5401da177e4SLinus Torvalds break; 5411da177e4SLinus Torvalds default: 542*4c9f1d3eSTakashi Iwai snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n", 5431da177e4SLinus Torvalds chip->mpu_irq); 5441da177e4SLinus Torvalds goto __skip_mpu; 5451da177e4SLinus Torvalds } 5461da177e4SLinus Torvalds 5471da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 5481da177e4SLinus Torvalds (chip->mpu_port <= 0) ? 0x00 : 5491da177e4SLinus Torvalds 0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3, 5501da177e4SLinus Torvalds 0xf8); 5511da177e4SLinus Torvalds } 5521da177e4SLinus Torvalds __skip_mpu: 5531da177e4SLinus Torvalds 5541da177e4SLinus Torvalds return 0; 5551da177e4SLinus Torvalds } 5561da177e4SLinus Torvalds 5571da177e4SLinus Torvalds #ifdef OPTi93X 5581da177e4SLinus Torvalds 5597d12e780SDavid Howells static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id) 5601da177e4SLinus Torvalds { 5617779f75fSKrzysztof Helt struct snd_wss *codec = dev_id; 5629f240a55SKrzysztof Helt struct snd_opti9xx *chip = codec->card->private_data; 5631da177e4SLinus Torvalds unsigned char status; 5641da177e4SLinus Torvalds 5659f240a55SKrzysztof Helt status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11)); 5661da177e4SLinus Torvalds if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream) 5671da177e4SLinus Torvalds snd_pcm_period_elapsed(codec->playback_substream); 5681da177e4SLinus Torvalds if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) { 5697779f75fSKrzysztof Helt snd_wss_overrange(codec); 5701da177e4SLinus Torvalds snd_pcm_period_elapsed(codec->capture_substream); 5711da177e4SLinus Torvalds } 5721da177e4SLinus Torvalds outb(0x00, OPTi93X_PORT(codec, STATUS)); 5731da177e4SLinus Torvalds return IRQ_HANDLED; 5741da177e4SLinus Torvalds } 5751da177e4SLinus Torvalds 5761da177e4SLinus Torvalds #endif /* OPTi93X */ 5771da177e4SLinus Torvalds 5785e24c1c1STakashi Iwai static int __devinit snd_card_opti9xx_detect(struct snd_card *card, 5795e24c1c1STakashi Iwai struct snd_opti9xx *chip) 5801da177e4SLinus Torvalds { 5811da177e4SLinus Torvalds int i, err; 5821da177e4SLinus Torvalds 5831da177e4SLinus Torvalds #ifndef OPTi93X 5841da177e4SLinus Torvalds for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) { 5851da177e4SLinus Torvalds unsigned char value; 5861da177e4SLinus Torvalds 5871da177e4SLinus Torvalds if ((err = snd_opti9xx_init(chip, i)) < 0) 5881da177e4SLinus Torvalds return err; 5891da177e4SLinus Torvalds 5901da177e4SLinus Torvalds if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) 5911da177e4SLinus Torvalds continue; 5921da177e4SLinus Torvalds 5931da177e4SLinus Torvalds value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)); 5941da177e4SLinus Torvalds if ((value != 0xff) && (value != inb(chip->mc_base + 1))) 5951da177e4SLinus Torvalds if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1))) 5961da177e4SLinus Torvalds return 1; 5971da177e4SLinus Torvalds 598b1d5776dSTakashi Iwai release_and_free_resource(chip->res_mc_base); 5991da177e4SLinus Torvalds chip->res_mc_base = NULL; 6001da177e4SLinus Torvalds 6011da177e4SLinus Torvalds } 6021da177e4SLinus Torvalds #else /* OPTi93X */ 6031da177e4SLinus Torvalds for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) { 6041da177e4SLinus Torvalds unsigned long flags; 6051da177e4SLinus Torvalds unsigned char value; 6061da177e4SLinus Torvalds 6071da177e4SLinus Torvalds if ((err = snd_opti9xx_init(chip, i)) < 0) 6081da177e4SLinus Torvalds return err; 6091da177e4SLinus Torvalds 6101da177e4SLinus Torvalds if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL) 6111da177e4SLinus Torvalds continue; 6121da177e4SLinus Torvalds 6131da177e4SLinus Torvalds spin_lock_irqsave(&chip->lock, flags); 6141da177e4SLinus Torvalds outb(chip->password, chip->mc_base + chip->pwd_reg); 6151da177e4SLinus Torvalds outb(((chip->mc_indir_index & (1 << 8)) >> 4) | 6161da177e4SLinus Torvalds ((chip->mc_indir_index & 0xf0) >> 4), chip->mc_base); 6171da177e4SLinus Torvalds spin_unlock_irqrestore(&chip->lock, flags); 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)); 6201da177e4SLinus Torvalds snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value); 6211da177e4SLinus Torvalds if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value) 6221da177e4SLinus Torvalds return 1; 6231da177e4SLinus Torvalds 624b1d5776dSTakashi Iwai release_and_free_resource(chip->res_mc_base); 6251da177e4SLinus Torvalds chip->res_mc_base = NULL; 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds #endif /* OPTi93X */ 6281da177e4SLinus Torvalds 6291da177e4SLinus Torvalds return -ENODEV; 6301da177e4SLinus Torvalds } 6311da177e4SLinus Torvalds 6321da177e4SLinus Torvalds #ifdef CONFIG_PNP 6335e24c1c1STakashi Iwai static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip, 6345e24c1c1STakashi Iwai struct pnp_card_link *card, 6351da177e4SLinus Torvalds const struct pnp_card_device_id *pid) 6361da177e4SLinus Torvalds { 6371da177e4SLinus Torvalds struct pnp_dev *pdev; 6381da177e4SLinus Torvalds int err; 6391da177e4SLinus Torvalds 6401da177e4SLinus Torvalds chip->dev = pnp_request_card_device(card, pid->devs[0].id, NULL); 641109c53f8SRene Herman if (chip->dev == NULL) 6421da177e4SLinus Torvalds return -EBUSY; 643109c53f8SRene Herman 6441da177e4SLinus Torvalds chip->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL); 6451da177e4SLinus Torvalds 6461da177e4SLinus Torvalds pdev = chip->dev; 6471da177e4SLinus Torvalds 6481da177e4SLinus Torvalds err = pnp_activate_dev(pdev); 6491da177e4SLinus Torvalds if (err < 0) { 6501da177e4SLinus Torvalds snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err); 6511da177e4SLinus Torvalds return err; 6521da177e4SLinus Torvalds } 6531da177e4SLinus Torvalds 6541da177e4SLinus Torvalds #ifdef OPTi93X 6551da177e4SLinus Torvalds port = pnp_port_start(pdev, 0) - 4; 6561ea73412STakashi Iwai fm_port = pnp_port_start(pdev, 1) + 8; 6571da177e4SLinus Torvalds #else 6581da177e4SLinus Torvalds if (pid->driver_data != 0x0924) 6591da177e4SLinus Torvalds port = pnp_port_start(pdev, 1); 6601ea73412STakashi Iwai fm_port = pnp_port_start(pdev, 2) + 8; 6611da177e4SLinus Torvalds #endif /* OPTi93X */ 6621da177e4SLinus Torvalds irq = pnp_irq(pdev, 0); 6631da177e4SLinus Torvalds dma1 = pnp_dma(pdev, 0); 6641da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X) 6651da177e4SLinus Torvalds dma2 = pnp_dma(pdev, 1); 6661da177e4SLinus Torvalds #endif /* CS4231 || OPTi93X */ 6671da177e4SLinus Torvalds 6681da177e4SLinus Torvalds pdev = chip->devmpu; 6691da177e4SLinus Torvalds if (pdev && mpu_port > 0) { 6701da177e4SLinus Torvalds err = pnp_activate_dev(pdev); 6711da177e4SLinus Torvalds if (err < 0) { 6721da177e4SLinus Torvalds snd_printk(KERN_ERR "AUDIO pnp configure failure\n"); 6731da177e4SLinus Torvalds mpu_port = -1; 6741da177e4SLinus Torvalds chip->devmpu = NULL; 6751da177e4SLinus Torvalds } else { 6761da177e4SLinus Torvalds mpu_port = pnp_port_start(pdev, 0); 6771da177e4SLinus Torvalds mpu_irq = pnp_irq(pdev, 0); 6781da177e4SLinus Torvalds } 6791da177e4SLinus Torvalds } 6801da177e4SLinus Torvalds return pid->driver_data; 6811da177e4SLinus Torvalds } 6821da177e4SLinus Torvalds #endif /* CONFIG_PNP */ 6831da177e4SLinus Torvalds 684346c7a68STakashi Iwai static void snd_card_opti9xx_free(struct snd_card *card) 6851da177e4SLinus Torvalds { 68699a0b768STakashi Iwai struct snd_opti9xx *chip = card->private_data; 6871da177e4SLinus Torvalds 6889f240a55SKrzysztof Helt if (chip) { 6899f240a55SKrzysztof Helt #ifdef OPTi93X 6907779f75fSKrzysztof Helt struct snd_wss *codec = chip->codec; 69182af6bc0STakashi Iwai if (codec && codec->irq > 0) { 6929f240a55SKrzysztof Helt disable_irq(codec->irq); 6939f240a55SKrzysztof Helt free_irq(codec->irq, codec); 6949f240a55SKrzysztof Helt } 6959f240a55SKrzysztof Helt #endif 696b1d5776dSTakashi Iwai release_and_free_resource(chip->res_mc_base); 6971da177e4SLinus Torvalds } 6989f240a55SKrzysztof Helt } 6991da177e4SLinus Torvalds 7005e24c1c1STakashi Iwai static int __devinit snd_opti9xx_probe(struct snd_card *card) 7011da177e4SLinus Torvalds { 7021da177e4SLinus Torvalds static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1}; 7031da177e4SLinus Torvalds int error; 70499a0b768STakashi Iwai struct snd_opti9xx *chip = card->private_data; 7057779f75fSKrzysztof Helt struct snd_wss *codec; 7069f240a55SKrzysztof Helt #ifdef CS4231 707346c7a68STakashi Iwai struct snd_timer *timer; 7089f240a55SKrzysztof Helt #endif 709346c7a68STakashi Iwai struct snd_pcm *pcm; 710346c7a68STakashi Iwai struct snd_rawmidi *rmidi; 711346c7a68STakashi Iwai struct snd_hwdep *synth; 7121da177e4SLinus Torvalds 7131da177e4SLinus Torvalds if (! chip->res_mc_base && 71499a0b768STakashi Iwai (chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, 71599a0b768STakashi Iwai "OPTi9xx MC")) == NULL) 7161da177e4SLinus Torvalds return -ENOMEM; 7171da177e4SLinus Torvalds 7181da177e4SLinus Torvalds chip->wss_base = port; 7191da177e4SLinus Torvalds chip->fm_port = fm_port; 7201da177e4SLinus Torvalds chip->mpu_port = mpu_port; 7211da177e4SLinus Torvalds chip->irq = irq; 7221da177e4SLinus Torvalds chip->mpu_irq = mpu_irq; 7231da177e4SLinus Torvalds chip->dma1 = dma1; 7241da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X) 7251da177e4SLinus Torvalds chip->dma2 = dma2; 726a0d9274cSRene Herman #else 727a0d9274cSRene Herman chip->dma2 = -1; 7281da177e4SLinus Torvalds #endif 7291da177e4SLinus Torvalds 7301da177e4SLinus Torvalds if (chip->wss_base == SNDRV_AUTO_PORT) { 7317779f75fSKrzysztof Helt chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4); 7327779f75fSKrzysztof Helt if (chip->wss_base < 0) { 733*4c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "unable to find a free WSS port\n"); 7341da177e4SLinus Torvalds return -EBUSY; 7351da177e4SLinus Torvalds } 7361da177e4SLinus Torvalds } 7377779f75fSKrzysztof Helt error = snd_opti9xx_configure(chip); 7387779f75fSKrzysztof Helt if (error) 7391da177e4SLinus Torvalds return error; 7401da177e4SLinus Torvalds 7417779f75fSKrzysztof Helt error = snd_wss_create(card, chip->wss_base + 4, -1, 7421da177e4SLinus Torvalds chip->irq, chip->dma1, chip->dma2, 743a0d9274cSRene Herman #ifdef OPTi93X 7447779f75fSKrzysztof Helt WSS_HW_OPTI93X, WSS_HWSHARE_IRQ, 745a0d9274cSRene Herman #else 746a0d9274cSRene Herman WSS_HW_DETECT, 0, 7479f240a55SKrzysztof Helt #endif 7487779f75fSKrzysztof Helt &codec); 7497779f75fSKrzysztof Helt if (error < 0) 7501da177e4SLinus Torvalds return error; 7519f240a55SKrzysztof Helt #ifdef OPTi93X 7529f240a55SKrzysztof Helt chip->codec = codec; 7539f240a55SKrzysztof Helt #endif 754ead893c0SKrzysztof Helt error = snd_wss_pcm(codec, 0, &pcm); 7555664daa1SKrzysztof Helt if (error < 0) 7565664daa1SKrzysztof Helt return error; 7577779f75fSKrzysztof Helt error = snd_wss_mixer(codec); 7587779f75fSKrzysztof Helt if (error < 0) 7591da177e4SLinus Torvalds return error; 7609f240a55SKrzysztof Helt #ifdef CS4231 7617779f75fSKrzysztof Helt error = snd_wss_timer(codec, 0, &timer); 7627779f75fSKrzysztof Helt if (error < 0) 7631da177e4SLinus Torvalds return error; 7645664daa1SKrzysztof Helt #endif 7655664daa1SKrzysztof Helt #ifdef OPTi93X 7669f240a55SKrzysztof Helt error = request_irq(chip->irq, snd_opti93x_interrupt, 7679f240a55SKrzysztof Helt IRQF_DISABLED, DEV_NAME" - WSS", codec); 7689f240a55SKrzysztof Helt if (error < 0) { 7699f240a55SKrzysztof Helt snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq); 7709f240a55SKrzysztof Helt return error; 7719f240a55SKrzysztof Helt } 7729f240a55SKrzysztof Helt #endif 7731da177e4SLinus Torvalds strcpy(card->driver, chip->name); 7741da177e4SLinus Torvalds sprintf(card->shortname, "OPTi %s", card->driver); 7751da177e4SLinus Torvalds #if defined(CS4231) || defined(OPTi93X) 7761da177e4SLinus Torvalds sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d", 7771da177e4SLinus Torvalds card->shortname, pcm->name, chip->wss_base + 4, 7781da177e4SLinus Torvalds chip->irq, chip->dma1, chip->dma2); 7791da177e4SLinus Torvalds #else 7801da177e4SLinus Torvalds sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d", 7811da177e4SLinus Torvalds card->shortname, pcm->name, chip->wss_base + 4, 7821da177e4SLinus Torvalds chip->irq, chip->dma1); 7831da177e4SLinus Torvalds #endif /* CS4231 || OPTi93X */ 7841da177e4SLinus Torvalds 7851da177e4SLinus Torvalds if (chip->mpu_port <= 0 || chip->mpu_port == SNDRV_AUTO_PORT) 7861da177e4SLinus Torvalds rmidi = NULL; 7871da177e4SLinus Torvalds else 7881da177e4SLinus Torvalds if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, 78965ca68b3SThomas Gleixner chip->mpu_port, 0, chip->mpu_irq, IRQF_DISABLED, 7901da177e4SLinus Torvalds &rmidi))) 79199a0b768STakashi Iwai snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", 79299a0b768STakashi Iwai chip->mpu_port); 7931da177e4SLinus Torvalds 7941da177e4SLinus Torvalds if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) { 795346c7a68STakashi Iwai struct snd_opl3 *opl3 = NULL; 7961da177e4SLinus Torvalds #ifndef OPTi93X 7971da177e4SLinus Torvalds if (chip->hardware == OPTi9XX_HW_82C928 || 7981da177e4SLinus Torvalds chip->hardware == OPTi9XX_HW_82C929 || 7991da177e4SLinus Torvalds chip->hardware == OPTi9XX_HW_82C924) { 800346c7a68STakashi Iwai struct snd_opl4 *opl4; 8011da177e4SLinus Torvalds /* assume we have an OPL4 */ 8021da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 8031da177e4SLinus Torvalds 0x20, 0x20); 8041da177e4SLinus Torvalds if (snd_opl4_create(card, 8051da177e4SLinus Torvalds chip->fm_port, 8061da177e4SLinus Torvalds chip->fm_port - 8, 8071da177e4SLinus Torvalds 2, &opl3, &opl4) < 0) { 8081da177e4SLinus Torvalds /* no luck, use OPL3 instead */ 8091da177e4SLinus Torvalds snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 8101da177e4SLinus Torvalds 0x00, 0x20); 8111da177e4SLinus Torvalds } 8121da177e4SLinus Torvalds } 8131da177e4SLinus Torvalds #endif /* !OPTi93X */ 8141da177e4SLinus Torvalds if (!opl3 && snd_opl3_create(card, 8151da177e4SLinus Torvalds chip->fm_port, 8161da177e4SLinus Torvalds chip->fm_port + 2, 8171da177e4SLinus Torvalds OPL3_HW_AUTO, 0, &opl3) < 0) { 81899a0b768STakashi Iwai snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n", 8191da177e4SLinus Torvalds chip->fm_port, chip->fm_port + 4 - 1); 8201da177e4SLinus Torvalds } 8211da177e4SLinus Torvalds if (opl3) { 822aa9c293aSKrzysztof Helt error = snd_opl3_hwdep_new(opl3, 0, 1, &synth); 823aa9c293aSKrzysztof Helt if (error < 0) 8241da177e4SLinus Torvalds return error; 8251da177e4SLinus Torvalds } 8261da177e4SLinus Torvalds } 8271da177e4SLinus Torvalds 82899a0b768STakashi Iwai return snd_card_register(card); 82999a0b768STakashi Iwai } 83099a0b768STakashi Iwai 83199a0b768STakashi Iwai static struct snd_card *snd_opti9xx_card_new(void) 83299a0b768STakashi Iwai { 83399a0b768STakashi Iwai struct snd_card *card; 83499a0b768STakashi Iwai 83599a0b768STakashi Iwai card = snd_card_new(index, id, THIS_MODULE, sizeof(struct snd_opti9xx)); 83699a0b768STakashi Iwai if (! card) 83799a0b768STakashi Iwai return NULL; 83899a0b768STakashi Iwai card->private_free = snd_card_opti9xx_free; 83999a0b768STakashi Iwai return card; 84099a0b768STakashi Iwai } 84199a0b768STakashi Iwai 8425e24c1c1STakashi Iwai static int __devinit snd_opti9xx_isa_match(struct device *devptr, 8435e24c1c1STakashi Iwai unsigned int dev) 8445e24c1c1STakashi Iwai { 845101f6f4bSTakashi Iwai #ifdef CONFIG_PNP 8465e24c1c1STakashi Iwai if (snd_opti9xx_pnp_is_probed) 8475e24c1c1STakashi Iwai return 0; 8485e24c1c1STakashi Iwai if (isapnp) 8495e24c1c1STakashi Iwai return 0; 850101f6f4bSTakashi Iwai #endif 8515e24c1c1STakashi Iwai return 1; 8525e24c1c1STakashi Iwai } 8535e24c1c1STakashi Iwai 8545e24c1c1STakashi Iwai static int __devinit snd_opti9xx_isa_probe(struct device *devptr, 8555e24c1c1STakashi Iwai unsigned int dev) 85699a0b768STakashi Iwai { 85799a0b768STakashi Iwai struct snd_card *card; 85899a0b768STakashi Iwai int error; 85999a0b768STakashi Iwai static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1}; 86099a0b768STakashi Iwai #ifdef OPTi93X 86199a0b768STakashi Iwai static int possible_irqs[] = {5, 9, 10, 11, 7, -1}; 86299a0b768STakashi Iwai #else 86399a0b768STakashi Iwai static int possible_irqs[] = {9, 10, 11, 7, -1}; 86499a0b768STakashi Iwai #endif /* OPTi93X */ 86599a0b768STakashi Iwai static int possible_mpu_irqs[] = {5, 9, 10, 7, -1}; 86699a0b768STakashi Iwai static int possible_dma1s[] = {3, 1, 0, -1}; 86799a0b768STakashi Iwai #if defined(CS4231) || defined(OPTi93X) 86899a0b768STakashi Iwai static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}}; 86999a0b768STakashi Iwai #endif /* CS4231 || OPTi93X */ 87099a0b768STakashi Iwai 87199a0b768STakashi Iwai if (mpu_port == SNDRV_AUTO_PORT) { 87299a0b768STakashi Iwai if ((mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) { 87399a0b768STakashi Iwai snd_printk(KERN_ERR "unable to find a free MPU401 port\n"); 87499a0b768STakashi Iwai return -EBUSY; 87599a0b768STakashi Iwai } 87699a0b768STakashi Iwai } 87799a0b768STakashi Iwai if (irq == SNDRV_AUTO_IRQ) { 87899a0b768STakashi Iwai if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { 87999a0b768STakashi Iwai snd_printk(KERN_ERR "unable to find a free IRQ\n"); 88099a0b768STakashi Iwai return -EBUSY; 88199a0b768STakashi Iwai } 88299a0b768STakashi Iwai } 88399a0b768STakashi Iwai if (mpu_irq == SNDRV_AUTO_IRQ) { 88499a0b768STakashi Iwai if ((mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) { 88599a0b768STakashi Iwai snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n"); 88699a0b768STakashi Iwai return -EBUSY; 88799a0b768STakashi Iwai } 88899a0b768STakashi Iwai } 88999a0b768STakashi Iwai if (dma1 == SNDRV_AUTO_DMA) { 89099a0b768STakashi Iwai if ((dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) { 89199a0b768STakashi Iwai snd_printk(KERN_ERR "unable to find a free DMA1\n"); 89299a0b768STakashi Iwai return -EBUSY; 89399a0b768STakashi Iwai } 89499a0b768STakashi Iwai } 89599a0b768STakashi Iwai #if defined(CS4231) || defined(OPTi93X) 89699a0b768STakashi Iwai if (dma2 == SNDRV_AUTO_DMA) { 89799a0b768STakashi Iwai if ((dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4])) < 0) { 898*4c9f1d3eSTakashi Iwai snd_printk(KERN_ERR "unable to find a free DMA2\n"); 89999a0b768STakashi Iwai return -EBUSY; 90099a0b768STakashi Iwai } 90199a0b768STakashi Iwai } 90299a0b768STakashi Iwai #endif 90399a0b768STakashi Iwai 90499a0b768STakashi Iwai card = snd_opti9xx_card_new(); 90599a0b768STakashi Iwai if (! card) 90699a0b768STakashi Iwai return -ENOMEM; 90799a0b768STakashi Iwai 90899a0b768STakashi Iwai if ((error = snd_card_opti9xx_detect(card, card->private_data)) < 0) { 9091da177e4SLinus Torvalds snd_card_free(card); 9101da177e4SLinus Torvalds return error; 9111da177e4SLinus Torvalds } 9125e24c1c1STakashi Iwai snd_card_set_dev(card, devptr); 91399a0b768STakashi Iwai if ((error = snd_opti9xx_probe(card)) < 0) { 91499a0b768STakashi Iwai snd_card_free(card); 91599a0b768STakashi Iwai return error; 91699a0b768STakashi Iwai } 9175e24c1c1STakashi Iwai dev_set_drvdata(devptr, card); 9181da177e4SLinus Torvalds return 0; 9191da177e4SLinus Torvalds } 9201da177e4SLinus Torvalds 9215e24c1c1STakashi Iwai static int __devexit snd_opti9xx_isa_remove(struct device *devptr, 9225e24c1c1STakashi Iwai unsigned int dev) 92399a0b768STakashi Iwai { 9245e24c1c1STakashi Iwai snd_card_free(dev_get_drvdata(devptr)); 9255e24c1c1STakashi Iwai dev_set_drvdata(devptr, NULL); 92699a0b768STakashi Iwai return 0; 92799a0b768STakashi Iwai } 92899a0b768STakashi Iwai 9295e24c1c1STakashi Iwai static struct isa_driver snd_opti9xx_driver = { 9305e24c1c1STakashi Iwai .match = snd_opti9xx_isa_match, 9315e24c1c1STakashi Iwai .probe = snd_opti9xx_isa_probe, 9325e24c1c1STakashi Iwai .remove = __devexit_p(snd_opti9xx_isa_remove), 93399a0b768STakashi Iwai /* FIXME: suspend/resume */ 93499a0b768STakashi Iwai .driver = { 93583c51c0aSRene Herman .name = DEV_NAME 93699a0b768STakashi Iwai }, 93799a0b768STakashi Iwai }; 93899a0b768STakashi Iwai 9391da177e4SLinus Torvalds #ifdef CONFIG_PNP 9405e24c1c1STakashi Iwai static int __devinit snd_opti9xx_pnp_probe(struct pnp_card_link *pcard, 94199a0b768STakashi Iwai const struct pnp_card_device_id *pid) 94299a0b768STakashi Iwai { 94399a0b768STakashi Iwai struct snd_card *card; 94499a0b768STakashi Iwai int error, hw; 94599a0b768STakashi Iwai struct snd_opti9xx *chip; 94699a0b768STakashi Iwai 94799a0b768STakashi Iwai if (snd_opti9xx_pnp_is_probed) 94899a0b768STakashi Iwai return -EBUSY; 94999a0b768STakashi Iwai if (! isapnp) 95099a0b768STakashi Iwai return -ENODEV; 95199a0b768STakashi Iwai card = snd_opti9xx_card_new(); 95299a0b768STakashi Iwai if (! card) 95399a0b768STakashi Iwai return -ENOMEM; 95499a0b768STakashi Iwai chip = card->private_data; 95599a0b768STakashi Iwai 95699a0b768STakashi Iwai hw = snd_card_opti9xx_pnp(chip, pcard, pid); 95799a0b768STakashi Iwai switch (hw) { 95899a0b768STakashi Iwai case 0x0924: 95999a0b768STakashi Iwai hw = OPTi9XX_HW_82C924; 96099a0b768STakashi Iwai break; 96199a0b768STakashi Iwai case 0x0925: 96299a0b768STakashi Iwai hw = OPTi9XX_HW_82C925; 96399a0b768STakashi Iwai break; 96499a0b768STakashi Iwai case 0x0931: 96599a0b768STakashi Iwai hw = OPTi9XX_HW_82C931; 96699a0b768STakashi Iwai break; 96799a0b768STakashi Iwai default: 96899a0b768STakashi Iwai snd_card_free(card); 96999a0b768STakashi Iwai return -ENODEV; 97099a0b768STakashi Iwai } 97199a0b768STakashi Iwai 97299a0b768STakashi Iwai if ((error = snd_opti9xx_init(chip, hw))) { 97399a0b768STakashi Iwai snd_card_free(card); 97499a0b768STakashi Iwai return error; 97599a0b768STakashi Iwai } 97699a0b768STakashi Iwai if (hw <= OPTi9XX_HW_82C930) 97799a0b768STakashi Iwai chip->mc_base -= 0x80; 97899a0b768STakashi Iwai snd_card_set_dev(card, &pcard->card->dev); 97999a0b768STakashi Iwai if ((error = snd_opti9xx_probe(card)) < 0) { 98099a0b768STakashi Iwai snd_card_free(card); 98199a0b768STakashi Iwai return error; 98299a0b768STakashi Iwai } 98399a0b768STakashi Iwai pnp_set_card_drvdata(pcard, card); 98499a0b768STakashi Iwai snd_opti9xx_pnp_is_probed = 1; 98599a0b768STakashi Iwai return 0; 98699a0b768STakashi Iwai } 98799a0b768STakashi Iwai 9881da177e4SLinus Torvalds static void __devexit snd_opti9xx_pnp_remove(struct pnp_card_link * pcard) 9891da177e4SLinus Torvalds { 99099a0b768STakashi Iwai snd_card_free(pnp_get_card_drvdata(pcard)); 99199a0b768STakashi Iwai pnp_set_card_drvdata(pcard, NULL); 99299a0b768STakashi Iwai snd_opti9xx_pnp_is_probed = 0; 9931da177e4SLinus Torvalds } 9941da177e4SLinus Torvalds 9951da177e4SLinus Torvalds static struct pnp_card_driver opti9xx_pnpc_driver = { 9961da177e4SLinus Torvalds .flags = PNP_DRIVER_RES_DISABLE, 9971da177e4SLinus Torvalds .name = "opti9xx", 9981da177e4SLinus Torvalds .id_table = snd_opti9xx_pnpids, 99999a0b768STakashi Iwai .probe = snd_opti9xx_pnp_probe, 10001da177e4SLinus Torvalds .remove = __devexit_p(snd_opti9xx_pnp_remove), 10011da177e4SLinus Torvalds }; 10021da177e4SLinus Torvalds #endif 10031da177e4SLinus Torvalds 100499a0b768STakashi Iwai #ifdef OPTi93X 100599a0b768STakashi Iwai #define CHIP_NAME "82C93x" 100699a0b768STakashi Iwai #else 100799a0b768STakashi Iwai #define CHIP_NAME "82C92x" 100899a0b768STakashi Iwai #endif 100999a0b768STakashi Iwai 10101da177e4SLinus Torvalds static int __init alsa_card_opti9xx_init(void) 10111da177e4SLinus Torvalds { 10120bbbc4caSTakashi Iwai #ifdef CONFIG_PNP 101399a0b768STakashi Iwai pnp_register_card_driver(&opti9xx_pnpc_driver); 101499a0b768STakashi Iwai if (snd_opti9xx_pnp_is_probed) 10151da177e4SLinus Torvalds return 0; 1016101f6f4bSTakashi Iwai pnp_unregister_card_driver(&opti9xx_pnpc_driver); 10170bbbc4caSTakashi Iwai #endif 10185e24c1c1STakashi Iwai return isa_register_driver(&snd_opti9xx_driver, 1); 10191da177e4SLinus Torvalds } 10201da177e4SLinus Torvalds 10211da177e4SLinus Torvalds static void __exit alsa_card_opti9xx_exit(void) 10221da177e4SLinus Torvalds { 1023f7a9275dSClemens Ladisch if (!snd_opti9xx_pnp_is_probed) { 10245e24c1c1STakashi Iwai isa_unregister_driver(&snd_opti9xx_driver); 10255e24c1c1STakashi Iwai return; 1026f7a9275dSClemens Ladisch } 10270bbbc4caSTakashi Iwai #ifdef CONFIG_PNP 10281da177e4SLinus Torvalds pnp_unregister_card_driver(&opti9xx_pnpc_driver); 10290bbbc4caSTakashi Iwai #endif 10301da177e4SLinus Torvalds } 10311da177e4SLinus Torvalds 10321da177e4SLinus Torvalds module_init(alsa_card_opti9xx_init) 10331da177e4SLinus Torvalds module_exit(alsa_card_opti9xx_exit) 1034