18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards. 48c2ecf20Sopenharmony_ci Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci Part of this code was developed at the Italian Ministry of Air Defence, 78c2ecf20Sopenharmony_ci Sixth Division (oh, che pace ...), Rome. 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci Thanks to Maria Grazia Pollarini, Salvatore Vassallo. 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci*/ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/isa.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/pnp.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/io.h> 218c2ecf20Sopenharmony_ci#include <asm/dma.h> 228c2ecf20Sopenharmony_ci#include <sound/core.h> 238c2ecf20Sopenharmony_ci#include <sound/tlv.h> 248c2ecf20Sopenharmony_ci#include <sound/wss.h> 258c2ecf20Sopenharmony_ci#include <sound/mpu401.h> 268c2ecf20Sopenharmony_ci#include <sound/opl3.h> 278c2ecf20Sopenharmony_ci#ifndef OPTi93X 288c2ecf20Sopenharmony_ci#include <sound/opl4.h> 298c2ecf20Sopenharmony_ci#endif 308c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IOPORT 318c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IRQ 328c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_DMA 338c2ecf20Sopenharmony_ci#include <sound/initval.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ciMODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); 368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 378c2ecf20Sopenharmony_ci#ifdef OPTi93X 388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OPTi93X"); 398c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{OPTi,82C931/3}}"); 408c2ecf20Sopenharmony_ci#else /* OPTi93X */ 418c2ecf20Sopenharmony_ci#ifdef CS4231 428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OPTi92X - CS4231"); 438c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (CS4231)}," 448c2ecf20Sopenharmony_ci "{OPTi,82C925 (CS4231)}}"); 458c2ecf20Sopenharmony_ci#else /* CS4231 */ 468c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OPTi92X - AD1848"); 478c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)}," 488c2ecf20Sopenharmony_ci "{OPTi,82C925 (AD1848)}," 498c2ecf20Sopenharmony_ci "{OAK,Mozart}}"); 508c2ecf20Sopenharmony_ci#endif /* CS4231 */ 518c2ecf20Sopenharmony_ci#endif /* OPTi93X */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ 548c2ecf20Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ 558c2ecf20Sopenharmony_ci//static bool enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */ 568c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 578c2ecf20Sopenharmony_cistatic bool isapnp = true; /* Enable ISA PnP detection */ 588c2ecf20Sopenharmony_ci#endif 598c2ecf20Sopenharmony_cistatic long port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */ 608c2ecf20Sopenharmony_cistatic long mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */ 618c2ecf20Sopenharmony_cistatic long fm_port = SNDRV_DEFAULT_PORT1; /* 0x388 */ 628c2ecf20Sopenharmony_cistatic int irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10,11 */ 638c2ecf20Sopenharmony_cistatic int mpu_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10 */ 648c2ecf20Sopenharmony_cistatic int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ 658c2ecf20Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 668c2ecf20Sopenharmony_cistatic int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ 678c2ecf20Sopenharmony_ci#endif /* CS4231 || OPTi93X */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cimodule_param(index, int, 0444); 708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for opti9xx based soundcard."); 718c2ecf20Sopenharmony_cimodule_param(id, charp, 0444); 728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for opti9xx based soundcard."); 738c2ecf20Sopenharmony_ci//module_param(enable, bool, 0444); 748c2ecf20Sopenharmony_ci//MODULE_PARM_DESC(enable, "Enable opti9xx soundcard."); 758c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 768c2ecf20Sopenharmony_cimodule_param(isapnp, bool, 0444); 778c2ecf20Sopenharmony_ciMODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard."); 788c2ecf20Sopenharmony_ci#endif 798c2ecf20Sopenharmony_cimodule_param_hw(port, long, ioport, 0444); 808c2ecf20Sopenharmony_ciMODULE_PARM_DESC(port, "WSS port # for opti9xx driver."); 818c2ecf20Sopenharmony_cimodule_param_hw(mpu_port, long, ioport, 0444); 828c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver."); 838c2ecf20Sopenharmony_cimodule_param_hw(fm_port, long, ioport, 0444); 848c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver."); 858c2ecf20Sopenharmony_cimodule_param_hw(irq, int, irq, 0444); 868c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver."); 878c2ecf20Sopenharmony_cimodule_param_hw(mpu_irq, int, irq, 0444); 888c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver."); 898c2ecf20Sopenharmony_cimodule_param_hw(dma1, int, dma, 0444); 908c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver."); 918c2ecf20Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 928c2ecf20Sopenharmony_cimodule_param_hw(dma2, int, dma, 0444); 938c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver."); 948c2ecf20Sopenharmony_ci#endif /* CS4231 || OPTi93X */ 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C928 1 978c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C929 2 988c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C924 3 998c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C925 4 1008c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C930 5 1018c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C931 6 1028c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C933 7 1038c2ecf20Sopenharmony_ci#define OPTi9XX_HW_LAST OPTi9XX_HW_82C933 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#define OPTi9XX_MC_REG(n) n 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#ifdef OPTi93X 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci#define OPTi93X_STATUS 0x02 1108c2ecf20Sopenharmony_ci#define OPTi93X_PORT(chip, r) ((chip)->port + OPTi93X_##r) 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#define OPTi93X_IRQ_PLAYBACK 0x04 1138c2ecf20Sopenharmony_ci#define OPTi93X_IRQ_CAPTURE 0x08 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#endif /* OPTi93X */ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistruct snd_opti9xx { 1188c2ecf20Sopenharmony_ci unsigned short hardware; 1198c2ecf20Sopenharmony_ci unsigned char password; 1208c2ecf20Sopenharmony_ci char name[7]; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci unsigned long mc_base; 1238c2ecf20Sopenharmony_ci struct resource *res_mc_base; 1248c2ecf20Sopenharmony_ci unsigned long mc_base_size; 1258c2ecf20Sopenharmony_ci#ifdef OPTi93X 1268c2ecf20Sopenharmony_ci unsigned long mc_indir_index; 1278c2ecf20Sopenharmony_ci struct resource *res_mc_indir; 1288c2ecf20Sopenharmony_ci#endif /* OPTi93X */ 1298c2ecf20Sopenharmony_ci struct snd_wss *codec; 1308c2ecf20Sopenharmony_ci unsigned long pwd_reg; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci spinlock_t lock; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci long wss_base; 1358c2ecf20Sopenharmony_ci int irq; 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int snd_opti9xx_pnp_is_probed; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic const struct pnp_card_device_id snd_opti9xx_pnpids[] = { 1438c2ecf20Sopenharmony_ci#ifndef OPTi93X 1448c2ecf20Sopenharmony_ci /* OPTi 82C924 */ 1458c2ecf20Sopenharmony_ci { .id = "OPT0924", 1468c2ecf20Sopenharmony_ci .devs = { { "OPT0000" }, { "OPT0002" }, { "OPT0005" } }, 1478c2ecf20Sopenharmony_ci .driver_data = 0x0924 }, 1488c2ecf20Sopenharmony_ci /* OPTi 82C925 */ 1498c2ecf20Sopenharmony_ci { .id = "OPT0925", 1508c2ecf20Sopenharmony_ci .devs = { { "OPT9250" }, { "OPT0002" }, { "OPT0005" } }, 1518c2ecf20Sopenharmony_ci .driver_data = 0x0925 }, 1528c2ecf20Sopenharmony_ci#else 1538c2ecf20Sopenharmony_ci /* OPTi 82C931/3 */ 1548c2ecf20Sopenharmony_ci { .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } }, 1558c2ecf20Sopenharmony_ci .driver_data = 0x0931 }, 1568c2ecf20Sopenharmony_ci#endif /* OPTi93X */ 1578c2ecf20Sopenharmony_ci { .id = "" } 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci#endif /* CONFIG_PNP */ 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci#define DEV_NAME KBUILD_MODNAME 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic const char * const snd_opti9xx_names[] = { 1678c2ecf20Sopenharmony_ci "unknown", 1688c2ecf20Sopenharmony_ci "82C928", "82C929", 1698c2ecf20Sopenharmony_ci "82C924", "82C925", 1708c2ecf20Sopenharmony_ci "82C930", "82C931", "82C933" 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int snd_opti9xx_init(struct snd_opti9xx *chip, 1748c2ecf20Sopenharmony_ci unsigned short hardware) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci chip->hardware = hardware; 1798c2ecf20Sopenharmony_ci strcpy(chip->name, snd_opti9xx_names[hardware]); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci spin_lock_init(&chip->lock); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci chip->irq = -1; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci#ifndef OPTi93X 1868c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 1878c2ecf20Sopenharmony_ci if (isapnp && chip->mc_base) 1888c2ecf20Sopenharmony_ci /* PnP resource gives the least 10 bits */ 1898c2ecf20Sopenharmony_ci chip->mc_base |= 0xc00; 1908c2ecf20Sopenharmony_ci else 1918c2ecf20Sopenharmony_ci#endif /* CONFIG_PNP */ 1928c2ecf20Sopenharmony_ci { 1938c2ecf20Sopenharmony_ci chip->mc_base = 0xf8c; 1948c2ecf20Sopenharmony_ci chip->mc_base_size = opti9xx_mc_size[hardware]; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci#else 1978c2ecf20Sopenharmony_ci chip->mc_base_size = opti9xx_mc_size[hardware]; 1988c2ecf20Sopenharmony_ci#endif 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci switch (hardware) { 2018c2ecf20Sopenharmony_ci#ifndef OPTi93X 2028c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C928: 2038c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C929: 2048c2ecf20Sopenharmony_ci chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3; 2058c2ecf20Sopenharmony_ci chip->pwd_reg = 3; 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C924: 2098c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C925: 2108c2ecf20Sopenharmony_ci chip->password = 0xe5; 2118c2ecf20Sopenharmony_ci chip->pwd_reg = 3; 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci#else /* OPTi93X */ 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C930: 2168c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C931: 2178c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C933: 2188c2ecf20Sopenharmony_ci chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d; 2198c2ecf20Sopenharmony_ci if (!chip->mc_indir_index) 2208c2ecf20Sopenharmony_ci chip->mc_indir_index = 0xe0e; 2218c2ecf20Sopenharmony_ci chip->password = 0xe4; 2228c2ecf20Sopenharmony_ci chip->pwd_reg = 0; 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci#endif /* OPTi93X */ 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci default: 2278c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "chip %d not supported\n", hardware); 2288c2ecf20Sopenharmony_ci return -ENODEV; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic unsigned char snd_opti9xx_read(struct snd_opti9xx *chip, 2348c2ecf20Sopenharmony_ci unsigned char reg) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci unsigned long flags; 2378c2ecf20Sopenharmony_ci unsigned char retval = 0xff; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 2408c2ecf20Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci switch (chip->hardware) { 2438c2ecf20Sopenharmony_ci#ifndef OPTi93X 2448c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C924: 2458c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C925: 2468c2ecf20Sopenharmony_ci if (reg > 7) { 2478c2ecf20Sopenharmony_ci outb(reg, chip->mc_base + 8); 2488c2ecf20Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 2498c2ecf20Sopenharmony_ci retval = inb(chip->mc_base + 9); 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci fallthrough; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C928: 2558c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C929: 2568c2ecf20Sopenharmony_ci retval = inb(chip->mc_base + reg); 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci#else /* OPTi93X */ 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C930: 2618c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C931: 2628c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C933: 2638c2ecf20Sopenharmony_ci outb(reg, chip->mc_indir_index); 2648c2ecf20Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 2658c2ecf20Sopenharmony_ci retval = inb(chip->mc_indir_index + 1); 2668c2ecf20Sopenharmony_ci break; 2678c2ecf20Sopenharmony_ci#endif /* OPTi93X */ 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci default: 2708c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 2748c2ecf20Sopenharmony_ci return retval; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg, 2788c2ecf20Sopenharmony_ci unsigned char value) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci unsigned long flags; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 2838c2ecf20Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci switch (chip->hardware) { 2868c2ecf20Sopenharmony_ci#ifndef OPTi93X 2878c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C924: 2888c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C925: 2898c2ecf20Sopenharmony_ci if (reg > 7) { 2908c2ecf20Sopenharmony_ci outb(reg, chip->mc_base + 8); 2918c2ecf20Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 2928c2ecf20Sopenharmony_ci outb(value, chip->mc_base + 9); 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci fallthrough; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C928: 2988c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C929: 2998c2ecf20Sopenharmony_ci outb(value, chip->mc_base + reg); 3008c2ecf20Sopenharmony_ci break; 3018c2ecf20Sopenharmony_ci#else /* OPTi93X */ 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C930: 3048c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C931: 3058c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C933: 3068c2ecf20Sopenharmony_ci outb(reg, chip->mc_indir_index); 3078c2ecf20Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 3088c2ecf20Sopenharmony_ci outb(value, chip->mc_indir_index + 1); 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci#endif /* OPTi93X */ 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci default: 3138c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic inline void snd_opti9xx_write_mask(struct snd_opti9xx *chip, 3218c2ecf20Sopenharmony_ci unsigned char reg, unsigned char value, unsigned char mask) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci unsigned char oldval = snd_opti9xx_read(chip, reg); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci snd_opti9xx_write(chip, reg, (oldval & ~mask) | (value & mask)); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int snd_opti9xx_configure(struct snd_opti9xx *chip, 3298c2ecf20Sopenharmony_ci long port, 3308c2ecf20Sopenharmony_ci int irq, int dma1, int dma2, 3318c2ecf20Sopenharmony_ci long mpu_port, int mpu_irq) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci unsigned char wss_base_bits; 3348c2ecf20Sopenharmony_ci unsigned char irq_bits; 3358c2ecf20Sopenharmony_ci unsigned char dma_bits; 3368c2ecf20Sopenharmony_ci unsigned char mpu_port_bits = 0; 3378c2ecf20Sopenharmony_ci unsigned char mpu_irq_bits; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci switch (chip->hardware) { 3408c2ecf20Sopenharmony_ci#ifndef OPTi93X 3418c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C924: 3428c2ecf20Sopenharmony_ci /* opti 929 mode (?), OPL3 clock output, audio enable */ 3438c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc); 3448c2ecf20Sopenharmony_ci /* enable wave audio */ 3458c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02); 3468c2ecf20Sopenharmony_ci fallthrough; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C925: 3498c2ecf20Sopenharmony_ci /* enable WSS mode */ 3508c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); 3518c2ecf20Sopenharmony_ci /* OPL3 FM synthesis */ 3528c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); 3538c2ecf20Sopenharmony_ci /* disable Sound Blaster IRQ and DMA */ 3548c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff); 3558c2ecf20Sopenharmony_ci#ifdef CS4231 3568c2ecf20Sopenharmony_ci /* cs4231/4248 fix enabled */ 3578c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); 3588c2ecf20Sopenharmony_ci#else 3598c2ecf20Sopenharmony_ci /* cs4231/4248 fix disabled */ 3608c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); 3618c2ecf20Sopenharmony_ci#endif /* CS4231 */ 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C928: 3658c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C929: 3668c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); 3678c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); 3688c2ecf20Sopenharmony_ci /* 3698c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae); 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c); 3728c2ecf20Sopenharmony_ci#ifdef CS4231 3738c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); 3748c2ecf20Sopenharmony_ci#else 3758c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); 3768c2ecf20Sopenharmony_ci#endif /* CS4231 */ 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci#else /* OPTi93X */ 3808c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C931: 3818c2ecf20Sopenharmony_ci /* disable 3D sound (set GPIO1 as output, low) */ 3828c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(20), 0x04, 0x0c); 3838c2ecf20Sopenharmony_ci fallthrough; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C933: 3868c2ecf20Sopenharmony_ci /* 3878c2ecf20Sopenharmony_ci * The BTC 1817DW has QS1000 wavetable which is connected 3888c2ecf20Sopenharmony_ci * to the serial digital input of the OPTI931. 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(21), 0x82, 0xff); 3918c2ecf20Sopenharmony_ci /* 3928c2ecf20Sopenharmony_ci * This bit sets OPTI931 to automaticaly select FM 3938c2ecf20Sopenharmony_ci * or digital input signal. 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01); 3968c2ecf20Sopenharmony_ci fallthrough; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C930: 3998c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03); 4008c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff); 4018c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 | 4028c2ecf20Sopenharmony_ci (chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04), 4038c2ecf20Sopenharmony_ci 0x34); 4048c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf); 4058c2ecf20Sopenharmony_ci break; 4068c2ecf20Sopenharmony_ci#endif /* OPTi93X */ 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci default: 4098c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 4108c2ecf20Sopenharmony_ci return -EINVAL; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* PnP resource says it decodes only 10 bits of address */ 4148c2ecf20Sopenharmony_ci switch (port & 0x3ff) { 4158c2ecf20Sopenharmony_ci case 0x130: 4168c2ecf20Sopenharmony_ci chip->wss_base = 0x530; 4178c2ecf20Sopenharmony_ci wss_base_bits = 0x00; 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci case 0x204: 4208c2ecf20Sopenharmony_ci chip->wss_base = 0x604; 4218c2ecf20Sopenharmony_ci wss_base_bits = 0x03; 4228c2ecf20Sopenharmony_ci break; 4238c2ecf20Sopenharmony_ci case 0x280: 4248c2ecf20Sopenharmony_ci chip->wss_base = 0xe80; 4258c2ecf20Sopenharmony_ci wss_base_bits = 0x01; 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci case 0x340: 4288c2ecf20Sopenharmony_ci chip->wss_base = 0xf40; 4298c2ecf20Sopenharmony_ci wss_base_bits = 0x02; 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci default: 4328c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", port); 4338c2ecf20Sopenharmony_ci goto __skip_base; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci__skip_base: 4388c2ecf20Sopenharmony_ci switch (irq) { 4398c2ecf20Sopenharmony_ci//#ifdef OPTi93X 4408c2ecf20Sopenharmony_ci case 5: 4418c2ecf20Sopenharmony_ci irq_bits = 0x05; 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci//#endif /* OPTi93X */ 4448c2ecf20Sopenharmony_ci case 7: 4458c2ecf20Sopenharmony_ci irq_bits = 0x01; 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci case 9: 4488c2ecf20Sopenharmony_ci irq_bits = 0x02; 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci case 10: 4518c2ecf20Sopenharmony_ci irq_bits = 0x03; 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci case 11: 4548c2ecf20Sopenharmony_ci irq_bits = 0x04; 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci default: 4578c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "WSS irq # %d not valid\n", irq); 4588c2ecf20Sopenharmony_ci goto __skip_resources; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci switch (dma1) { 4628c2ecf20Sopenharmony_ci case 0: 4638c2ecf20Sopenharmony_ci dma_bits = 0x01; 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci case 1: 4668c2ecf20Sopenharmony_ci dma_bits = 0x02; 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci case 3: 4698c2ecf20Sopenharmony_ci dma_bits = 0x03; 4708c2ecf20Sopenharmony_ci break; 4718c2ecf20Sopenharmony_ci default: 4728c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n", dma1); 4738c2ecf20Sopenharmony_ci goto __skip_resources; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 4778c2ecf20Sopenharmony_ci if (dma1 == dma2) { 4788c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "don't want to share dmas\n"); 4798c2ecf20Sopenharmony_ci return -EBUSY; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci switch (dma2) { 4838c2ecf20Sopenharmony_ci case 0: 4848c2ecf20Sopenharmony_ci case 1: 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci default: 4878c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n", dma2); 4888c2ecf20Sopenharmony_ci goto __skip_resources; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci dma_bits |= 0x04; 4918c2ecf20Sopenharmony_ci#endif /* CS4231 || OPTi93X */ 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci#ifndef OPTi93X 4948c2ecf20Sopenharmony_ci outb(irq_bits << 3 | dma_bits, chip->wss_base); 4958c2ecf20Sopenharmony_ci#else /* OPTi93X */ 4968c2ecf20Sopenharmony_ci snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits)); 4978c2ecf20Sopenharmony_ci#endif /* OPTi93X */ 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci__skip_resources: 5008c2ecf20Sopenharmony_ci if (chip->hardware > OPTi9XX_HW_82C928) { 5018c2ecf20Sopenharmony_ci switch (mpu_port) { 5028c2ecf20Sopenharmony_ci case 0: 5038c2ecf20Sopenharmony_ci case -1: 5048c2ecf20Sopenharmony_ci break; 5058c2ecf20Sopenharmony_ci case 0x300: 5068c2ecf20Sopenharmony_ci mpu_port_bits = 0x03; 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci case 0x310: 5098c2ecf20Sopenharmony_ci mpu_port_bits = 0x02; 5108c2ecf20Sopenharmony_ci break; 5118c2ecf20Sopenharmony_ci case 0x320: 5128c2ecf20Sopenharmony_ci mpu_port_bits = 0x01; 5138c2ecf20Sopenharmony_ci break; 5148c2ecf20Sopenharmony_ci case 0x330: 5158c2ecf20Sopenharmony_ci mpu_port_bits = 0x00; 5168c2ecf20Sopenharmony_ci break; 5178c2ecf20Sopenharmony_ci default: 5188c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING 5198c2ecf20Sopenharmony_ci "MPU-401 port 0x%lx not valid\n", mpu_port); 5208c2ecf20Sopenharmony_ci goto __skip_mpu; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci switch (mpu_irq) { 5248c2ecf20Sopenharmony_ci case 5: 5258c2ecf20Sopenharmony_ci mpu_irq_bits = 0x02; 5268c2ecf20Sopenharmony_ci break; 5278c2ecf20Sopenharmony_ci case 7: 5288c2ecf20Sopenharmony_ci mpu_irq_bits = 0x03; 5298c2ecf20Sopenharmony_ci break; 5308c2ecf20Sopenharmony_ci case 9: 5318c2ecf20Sopenharmony_ci mpu_irq_bits = 0x00; 5328c2ecf20Sopenharmony_ci break; 5338c2ecf20Sopenharmony_ci case 10: 5348c2ecf20Sopenharmony_ci mpu_irq_bits = 0x01; 5358c2ecf20Sopenharmony_ci break; 5368c2ecf20Sopenharmony_ci default: 5378c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n", 5388c2ecf20Sopenharmony_ci mpu_irq); 5398c2ecf20Sopenharmony_ci goto __skip_mpu; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 5438c2ecf20Sopenharmony_ci (mpu_port <= 0) ? 0x00 : 5448c2ecf20Sopenharmony_ci 0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3, 5458c2ecf20Sopenharmony_ci 0xf8); 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci__skip_mpu: 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci return 0; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci#ifdef OPTi93X 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_step, -9300, 300, 0); 5558c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0); 5568c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_4bit_12db_max, -3300, 300, 0); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_opti93x_controls[] = { 5598c2ecf20Sopenharmony_ciWSS_DOUBLE("Master Playback Switch", 0, 5608c2ecf20Sopenharmony_ci OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1), 5618c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("Master Playback Volume", 0, 5628c2ecf20Sopenharmony_ci OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1, 5638c2ecf20Sopenharmony_ci db_scale_5bit_3db_step), 5648c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("PCM Playback Volume", 0, 5658c2ecf20Sopenharmony_ci CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1, 5668c2ecf20Sopenharmony_ci db_scale_5bit), 5678c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("FM Playback Volume", 0, 5688c2ecf20Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1, 5698c2ecf20Sopenharmony_ci db_scale_4bit_12db_max), 5708c2ecf20Sopenharmony_ciWSS_DOUBLE("Line Playback Switch", 0, 5718c2ecf20Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), 5728c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("Line Playback Volume", 0, 5738c2ecf20Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1, 5748c2ecf20Sopenharmony_ci db_scale_4bit_12db_max), 5758c2ecf20Sopenharmony_ciWSS_DOUBLE("Mic Playback Switch", 0, 5768c2ecf20Sopenharmony_ci OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1), 5778c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("Mic Playback Volume", 0, 5788c2ecf20Sopenharmony_ci OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1, 5798c2ecf20Sopenharmony_ci db_scale_4bit_12db_max), 5808c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("CD Playback Volume", 0, 5818c2ecf20Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1, 5828c2ecf20Sopenharmony_ci db_scale_4bit_12db_max), 5838c2ecf20Sopenharmony_ciWSS_DOUBLE("Aux Playback Switch", 0, 5848c2ecf20Sopenharmony_ci OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1), 5858c2ecf20Sopenharmony_ciWSS_DOUBLE_TLV("Aux Playback Volume", 0, 5868c2ecf20Sopenharmony_ci OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1, 5878c2ecf20Sopenharmony_ci db_scale_4bit_12db_max), 5888c2ecf20Sopenharmony_ci}; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic int snd_opti93x_mixer(struct snd_wss *chip) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci struct snd_card *card; 5938c2ecf20Sopenharmony_ci unsigned int idx; 5948c2ecf20Sopenharmony_ci struct snd_ctl_elem_id id1, id2; 5958c2ecf20Sopenharmony_ci int err; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (snd_BUG_ON(!chip || !chip->pcm)) 5988c2ecf20Sopenharmony_ci return -EINVAL; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci card = chip->card; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci strcpy(card->mixername, chip->pcm->name); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci memset(&id1, 0, sizeof(id1)); 6058c2ecf20Sopenharmony_ci memset(&id2, 0, sizeof(id2)); 6068c2ecf20Sopenharmony_ci id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 6078c2ecf20Sopenharmony_ci /* reassign AUX0 switch to CD */ 6088c2ecf20Sopenharmony_ci strcpy(id1.name, "Aux Playback Switch"); 6098c2ecf20Sopenharmony_ci strcpy(id2.name, "CD Playback Switch"); 6108c2ecf20Sopenharmony_ci err = snd_ctl_rename_id(card, &id1, &id2); 6118c2ecf20Sopenharmony_ci if (err < 0) { 6128c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "Cannot rename opti93x control\n"); 6138c2ecf20Sopenharmony_ci return err; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci /* reassign AUX1 switch to FM */ 6168c2ecf20Sopenharmony_ci strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; 6178c2ecf20Sopenharmony_ci strcpy(id2.name, "FM Playback Switch"); 6188c2ecf20Sopenharmony_ci err = snd_ctl_rename_id(card, &id1, &id2); 6198c2ecf20Sopenharmony_ci if (err < 0) { 6208c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "Cannot rename opti93x control\n"); 6218c2ecf20Sopenharmony_ci return err; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci /* remove AUX1 volume */ 6248c2ecf20Sopenharmony_ci strcpy(id1.name, "Aux Playback Volume"); id1.index = 1; 6258c2ecf20Sopenharmony_ci snd_ctl_remove_id(card, &id1); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* Replace WSS volume controls with OPTi93x volume controls */ 6288c2ecf20Sopenharmony_ci id1.index = 0; 6298c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) { 6308c2ecf20Sopenharmony_ci strcpy(id1.name, snd_opti93x_controls[idx].name); 6318c2ecf20Sopenharmony_ci snd_ctl_remove_id(card, &id1); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci err = snd_ctl_add(card, 6348c2ecf20Sopenharmony_ci snd_ctl_new1(&snd_opti93x_controls[idx], chip)); 6358c2ecf20Sopenharmony_ci if (err < 0) 6368c2ecf20Sopenharmony_ci return err; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci return 0; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci struct snd_opti9xx *chip = dev_id; 6448c2ecf20Sopenharmony_ci struct snd_wss *codec = chip->codec; 6458c2ecf20Sopenharmony_ci unsigned char status; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (!codec) 6488c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11)); 6518c2ecf20Sopenharmony_ci if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream) 6528c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(codec->playback_substream); 6538c2ecf20Sopenharmony_ci if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) { 6548c2ecf20Sopenharmony_ci snd_wss_overrange(codec); 6558c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(codec->capture_substream); 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci outb(0x00, OPTi93X_PORT(codec, STATUS)); 6588c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci#endif /* OPTi93X */ 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic int snd_opti9xx_read_check(struct snd_opti9xx *chip) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci unsigned char value; 6668c2ecf20Sopenharmony_ci#ifdef OPTi93X 6678c2ecf20Sopenharmony_ci unsigned long flags; 6688c2ecf20Sopenharmony_ci#endif 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, 6718c2ecf20Sopenharmony_ci "OPTi9xx MC"); 6728c2ecf20Sopenharmony_ci if (chip->res_mc_base == NULL) 6738c2ecf20Sopenharmony_ci return -EBUSY; 6748c2ecf20Sopenharmony_ci#ifndef OPTi93X 6758c2ecf20Sopenharmony_ci value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)); 6768c2ecf20Sopenharmony_ci if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1))) 6778c2ecf20Sopenharmony_ci if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1))) 6788c2ecf20Sopenharmony_ci return 0; 6798c2ecf20Sopenharmony_ci#else /* OPTi93X */ 6808c2ecf20Sopenharmony_ci chip->res_mc_indir = request_region(chip->mc_indir_index, 2, 6818c2ecf20Sopenharmony_ci "OPTi93x MC"); 6828c2ecf20Sopenharmony_ci if (chip->res_mc_indir == NULL) 6838c2ecf20Sopenharmony_ci return -EBUSY; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 6868c2ecf20Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 6878c2ecf20Sopenharmony_ci outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base); 6888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)); 6918c2ecf20Sopenharmony_ci snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value); 6928c2ecf20Sopenharmony_ci if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value) 6938c2ecf20Sopenharmony_ci return 0; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci release_and_free_resource(chip->res_mc_indir); 6968c2ecf20Sopenharmony_ci chip->res_mc_indir = NULL; 6978c2ecf20Sopenharmony_ci#endif /* OPTi93X */ 6988c2ecf20Sopenharmony_ci release_and_free_resource(chip->res_mc_base); 6998c2ecf20Sopenharmony_ci chip->res_mc_base = NULL; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci return -ENODEV; 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic int snd_card_opti9xx_detect(struct snd_card *card, 7058c2ecf20Sopenharmony_ci struct snd_opti9xx *chip) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci int i, err; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci#ifndef OPTi93X 7108c2ecf20Sopenharmony_ci for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) { 7118c2ecf20Sopenharmony_ci#else 7128c2ecf20Sopenharmony_ci for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) { 7138c2ecf20Sopenharmony_ci#endif 7148c2ecf20Sopenharmony_ci err = snd_opti9xx_init(chip, i); 7158c2ecf20Sopenharmony_ci if (err < 0) 7168c2ecf20Sopenharmony_ci return err; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci err = snd_opti9xx_read_check(chip); 7198c2ecf20Sopenharmony_ci if (err == 0) 7208c2ecf20Sopenharmony_ci return 1; 7218c2ecf20Sopenharmony_ci#ifdef OPTi93X 7228c2ecf20Sopenharmony_ci chip->mc_indir_index = 0; 7238c2ecf20Sopenharmony_ci#endif 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci return -ENODEV; 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 7298c2ecf20Sopenharmony_cistatic int snd_card_opti9xx_pnp(struct snd_opti9xx *chip, 7308c2ecf20Sopenharmony_ci struct pnp_card_link *card, 7318c2ecf20Sopenharmony_ci const struct pnp_card_device_id *pid) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct pnp_dev *pdev; 7348c2ecf20Sopenharmony_ci int err; 7358c2ecf20Sopenharmony_ci struct pnp_dev *devmpu; 7368c2ecf20Sopenharmony_ci#ifndef OPTi93X 7378c2ecf20Sopenharmony_ci struct pnp_dev *devmc; 7388c2ecf20Sopenharmony_ci#endif 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci pdev = pnp_request_card_device(card, pid->devs[0].id, NULL); 7418c2ecf20Sopenharmony_ci if (pdev == NULL) 7428c2ecf20Sopenharmony_ci return -EBUSY; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci err = pnp_activate_dev(pdev); 7458c2ecf20Sopenharmony_ci if (err < 0) { 7468c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err); 7478c2ecf20Sopenharmony_ci return err; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci#ifdef OPTi93X 7518c2ecf20Sopenharmony_ci port = pnp_port_start(pdev, 0) - 4; 7528c2ecf20Sopenharmony_ci fm_port = pnp_port_start(pdev, 1) + 8; 7538c2ecf20Sopenharmony_ci /* adjust mc_indir_index - some cards report it at 0xe?d, 7548c2ecf20Sopenharmony_ci other at 0xe?c but it really is always at 0xe?e */ 7558c2ecf20Sopenharmony_ci chip->mc_indir_index = (pnp_port_start(pdev, 3) & ~0xf) | 0xe; 7568c2ecf20Sopenharmony_ci#else 7578c2ecf20Sopenharmony_ci devmc = pnp_request_card_device(card, pid->devs[2].id, NULL); 7588c2ecf20Sopenharmony_ci if (devmc == NULL) 7598c2ecf20Sopenharmony_ci return -EBUSY; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci err = pnp_activate_dev(devmc); 7628c2ecf20Sopenharmony_ci if (err < 0) { 7638c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "MC pnp configure failure: %d\n", err); 7648c2ecf20Sopenharmony_ci return err; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci port = pnp_port_start(pdev, 1); 7688c2ecf20Sopenharmony_ci fm_port = pnp_port_start(pdev, 2) + 8; 7698c2ecf20Sopenharmony_ci /* 7708c2ecf20Sopenharmony_ci * The MC(0) is never accessed and card does not 7718c2ecf20Sopenharmony_ci * include it in the PnP resource range. OPTI93x include it. 7728c2ecf20Sopenharmony_ci */ 7738c2ecf20Sopenharmony_ci chip->mc_base = pnp_port_start(devmc, 0) - 1; 7748c2ecf20Sopenharmony_ci chip->mc_base_size = pnp_port_len(devmc, 0) + 1; 7758c2ecf20Sopenharmony_ci#endif /* OPTi93X */ 7768c2ecf20Sopenharmony_ci irq = pnp_irq(pdev, 0); 7778c2ecf20Sopenharmony_ci dma1 = pnp_dma(pdev, 0); 7788c2ecf20Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 7798c2ecf20Sopenharmony_ci dma2 = pnp_dma(pdev, 1); 7808c2ecf20Sopenharmony_ci#endif /* CS4231 || OPTi93X */ 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci if (devmpu && mpu_port > 0) { 7858c2ecf20Sopenharmony_ci err = pnp_activate_dev(devmpu); 7868c2ecf20Sopenharmony_ci if (err < 0) { 7878c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "MPU401 pnp configure failure\n"); 7888c2ecf20Sopenharmony_ci mpu_port = -1; 7898c2ecf20Sopenharmony_ci } else { 7908c2ecf20Sopenharmony_ci mpu_port = pnp_port_start(devmpu, 0); 7918c2ecf20Sopenharmony_ci mpu_irq = pnp_irq(devmpu, 0); 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci return pid->driver_data; 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci#endif /* CONFIG_PNP */ 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_cistatic void snd_card_opti9xx_free(struct snd_card *card) 7998c2ecf20Sopenharmony_ci{ 8008c2ecf20Sopenharmony_ci struct snd_opti9xx *chip = card->private_data; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (chip) { 8038c2ecf20Sopenharmony_ci#ifdef OPTi93X 8048c2ecf20Sopenharmony_ci if (chip->irq > 0) { 8058c2ecf20Sopenharmony_ci disable_irq(chip->irq); 8068c2ecf20Sopenharmony_ci free_irq(chip->irq, chip); 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci release_and_free_resource(chip->res_mc_indir); 8098c2ecf20Sopenharmony_ci#endif 8108c2ecf20Sopenharmony_ci release_and_free_resource(chip->res_mc_base); 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic int snd_opti9xx_probe(struct snd_card *card) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci static const long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1}; 8178c2ecf20Sopenharmony_ci int error; 8188c2ecf20Sopenharmony_ci int xdma2; 8198c2ecf20Sopenharmony_ci struct snd_opti9xx *chip = card->private_data; 8208c2ecf20Sopenharmony_ci struct snd_wss *codec; 8218c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 8228c2ecf20Sopenharmony_ci struct snd_hwdep *synth; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 8258c2ecf20Sopenharmony_ci xdma2 = dma2; 8268c2ecf20Sopenharmony_ci#else 8278c2ecf20Sopenharmony_ci xdma2 = -1; 8288c2ecf20Sopenharmony_ci#endif 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (port == SNDRV_AUTO_PORT) { 8318c2ecf20Sopenharmony_ci port = snd_legacy_find_free_ioport(possible_ports, 4); 8328c2ecf20Sopenharmony_ci if (port < 0) { 8338c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free WSS port\n"); 8348c2ecf20Sopenharmony_ci return -EBUSY; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2, 8388c2ecf20Sopenharmony_ci mpu_port, mpu_irq); 8398c2ecf20Sopenharmony_ci if (error) 8408c2ecf20Sopenharmony_ci return error; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci error = snd_wss_create(card, chip->wss_base + 4, -1, irq, dma1, xdma2, 8438c2ecf20Sopenharmony_ci#ifdef OPTi93X 8448c2ecf20Sopenharmony_ci WSS_HW_OPTI93X, WSS_HWSHARE_IRQ, 8458c2ecf20Sopenharmony_ci#else 8468c2ecf20Sopenharmony_ci WSS_HW_DETECT, 0, 8478c2ecf20Sopenharmony_ci#endif 8488c2ecf20Sopenharmony_ci &codec); 8498c2ecf20Sopenharmony_ci if (error < 0) 8508c2ecf20Sopenharmony_ci return error; 8518c2ecf20Sopenharmony_ci chip->codec = codec; 8528c2ecf20Sopenharmony_ci error = snd_wss_pcm(codec, 0); 8538c2ecf20Sopenharmony_ci if (error < 0) 8548c2ecf20Sopenharmony_ci return error; 8558c2ecf20Sopenharmony_ci error = snd_wss_mixer(codec); 8568c2ecf20Sopenharmony_ci if (error < 0) 8578c2ecf20Sopenharmony_ci return error; 8588c2ecf20Sopenharmony_ci#ifdef OPTi93X 8598c2ecf20Sopenharmony_ci error = snd_opti93x_mixer(codec); 8608c2ecf20Sopenharmony_ci if (error < 0) 8618c2ecf20Sopenharmony_ci return error; 8628c2ecf20Sopenharmony_ci#endif 8638c2ecf20Sopenharmony_ci#ifdef CS4231 8648c2ecf20Sopenharmony_ci error = snd_wss_timer(codec, 0); 8658c2ecf20Sopenharmony_ci if (error < 0) 8668c2ecf20Sopenharmony_ci return error; 8678c2ecf20Sopenharmony_ci#endif 8688c2ecf20Sopenharmony_ci#ifdef OPTi93X 8698c2ecf20Sopenharmony_ci error = request_irq(irq, snd_opti93x_interrupt, 8708c2ecf20Sopenharmony_ci 0, DEV_NAME" - WSS", chip); 8718c2ecf20Sopenharmony_ci if (error < 0) { 8728c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", irq); 8738c2ecf20Sopenharmony_ci return error; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci#endif 8768c2ecf20Sopenharmony_ci chip->irq = irq; 8778c2ecf20Sopenharmony_ci card->sync_irq = chip->irq; 8788c2ecf20Sopenharmony_ci strcpy(card->driver, chip->name); 8798c2ecf20Sopenharmony_ci sprintf(card->shortname, "OPTi %s", card->driver); 8808c2ecf20Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 8818c2ecf20Sopenharmony_ci snprintf(card->longname, sizeof(card->longname), 8828c2ecf20Sopenharmony_ci "%s, %s at 0x%lx, irq %d, dma %d&%d", 8838c2ecf20Sopenharmony_ci card->shortname, codec->pcm->name, 8848c2ecf20Sopenharmony_ci chip->wss_base + 4, irq, dma1, xdma2); 8858c2ecf20Sopenharmony_ci#else 8868c2ecf20Sopenharmony_ci snprintf(card->longname, sizeof(card->longname), 8878c2ecf20Sopenharmony_ci "%s, %s at 0x%lx, irq %d, dma %d", 8888c2ecf20Sopenharmony_ci card->shortname, codec->pcm->name, chip->wss_base + 4, irq, 8898c2ecf20Sopenharmony_ci dma1); 8908c2ecf20Sopenharmony_ci#endif /* CS4231 || OPTi93X */ 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT) 8938c2ecf20Sopenharmony_ci rmidi = NULL; 8948c2ecf20Sopenharmony_ci else { 8958c2ecf20Sopenharmony_ci error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, 8968c2ecf20Sopenharmony_ci mpu_port, 0, mpu_irq, &rmidi); 8978c2ecf20Sopenharmony_ci if (error) 8988c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", 8998c2ecf20Sopenharmony_ci mpu_port); 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) { 9038c2ecf20Sopenharmony_ci struct snd_opl3 *opl3 = NULL; 9048c2ecf20Sopenharmony_ci#ifndef OPTi93X 9058c2ecf20Sopenharmony_ci if (chip->hardware == OPTi9XX_HW_82C928 || 9068c2ecf20Sopenharmony_ci chip->hardware == OPTi9XX_HW_82C929 || 9078c2ecf20Sopenharmony_ci chip->hardware == OPTi9XX_HW_82C924) { 9088c2ecf20Sopenharmony_ci struct snd_opl4 *opl4; 9098c2ecf20Sopenharmony_ci /* assume we have an OPL4 */ 9108c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 9118c2ecf20Sopenharmony_ci 0x20, 0x20); 9128c2ecf20Sopenharmony_ci if (snd_opl4_create(card, fm_port, fm_port - 8, 9138c2ecf20Sopenharmony_ci 2, &opl3, &opl4) < 0) { 9148c2ecf20Sopenharmony_ci /* no luck, use OPL3 instead */ 9158c2ecf20Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 9168c2ecf20Sopenharmony_ci 0x00, 0x20); 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci#endif /* !OPTi93X */ 9208c2ecf20Sopenharmony_ci if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2, 9218c2ecf20Sopenharmony_ci OPL3_HW_AUTO, 0, &opl3) < 0) { 9228c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n", 9238c2ecf20Sopenharmony_ci fm_port, fm_port + 4 - 1); 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci if (opl3) { 9268c2ecf20Sopenharmony_ci error = snd_opl3_hwdep_new(opl3, 0, 1, &synth); 9278c2ecf20Sopenharmony_ci if (error < 0) 9288c2ecf20Sopenharmony_ci return error; 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci return snd_card_register(card); 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic int snd_opti9xx_card_new(struct device *pdev, struct snd_card **cardp) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct snd_card *card; 9388c2ecf20Sopenharmony_ci int err; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci err = snd_card_new(pdev, index, id, THIS_MODULE, 9418c2ecf20Sopenharmony_ci sizeof(struct snd_opti9xx), &card); 9428c2ecf20Sopenharmony_ci if (err < 0) 9438c2ecf20Sopenharmony_ci return err; 9448c2ecf20Sopenharmony_ci card->private_free = snd_card_opti9xx_free; 9458c2ecf20Sopenharmony_ci *cardp = card; 9468c2ecf20Sopenharmony_ci return 0; 9478c2ecf20Sopenharmony_ci} 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_cistatic int snd_opti9xx_isa_match(struct device *devptr, 9508c2ecf20Sopenharmony_ci unsigned int dev) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 9538c2ecf20Sopenharmony_ci if (snd_opti9xx_pnp_is_probed) 9548c2ecf20Sopenharmony_ci return 0; 9558c2ecf20Sopenharmony_ci if (isapnp) 9568c2ecf20Sopenharmony_ci return 0; 9578c2ecf20Sopenharmony_ci#endif 9588c2ecf20Sopenharmony_ci return 1; 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_cistatic int snd_opti9xx_isa_probe(struct device *devptr, 9628c2ecf20Sopenharmony_ci unsigned int dev) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci struct snd_card *card; 9658c2ecf20Sopenharmony_ci int error; 9668c2ecf20Sopenharmony_ci static const long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1}; 9678c2ecf20Sopenharmony_ci#ifdef OPTi93X 9688c2ecf20Sopenharmony_ci static const int possible_irqs[] = {5, 9, 10, 11, 7, -1}; 9698c2ecf20Sopenharmony_ci#else 9708c2ecf20Sopenharmony_ci static const int possible_irqs[] = {9, 10, 11, 7, -1}; 9718c2ecf20Sopenharmony_ci#endif /* OPTi93X */ 9728c2ecf20Sopenharmony_ci static const int possible_mpu_irqs[] = {5, 9, 10, 7, -1}; 9738c2ecf20Sopenharmony_ci static const int possible_dma1s[] = {3, 1, 0, -1}; 9748c2ecf20Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 9758c2ecf20Sopenharmony_ci static const int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}}; 9768c2ecf20Sopenharmony_ci#endif /* CS4231 || OPTi93X */ 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (mpu_port == SNDRV_AUTO_PORT) { 9798c2ecf20Sopenharmony_ci if ((mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) { 9808c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free MPU401 port\n"); 9818c2ecf20Sopenharmony_ci return -EBUSY; 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci if (irq == SNDRV_AUTO_IRQ) { 9858c2ecf20Sopenharmony_ci if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) { 9868c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free IRQ\n"); 9878c2ecf20Sopenharmony_ci return -EBUSY; 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci if (mpu_irq == SNDRV_AUTO_IRQ) { 9918c2ecf20Sopenharmony_ci if ((mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) { 9928c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n"); 9938c2ecf20Sopenharmony_ci return -EBUSY; 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci if (dma1 == SNDRV_AUTO_DMA) { 9978c2ecf20Sopenharmony_ci if ((dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) { 9988c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free DMA1\n"); 9998c2ecf20Sopenharmony_ci return -EBUSY; 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 10038c2ecf20Sopenharmony_ci if (dma2 == SNDRV_AUTO_DMA) { 10048c2ecf20Sopenharmony_ci if ((dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4])) < 0) { 10058c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free DMA2\n"); 10068c2ecf20Sopenharmony_ci return -EBUSY; 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci#endif 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci error = snd_opti9xx_card_new(devptr, &card); 10128c2ecf20Sopenharmony_ci if (error < 0) 10138c2ecf20Sopenharmony_ci return error; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci if ((error = snd_card_opti9xx_detect(card, card->private_data)) < 0) { 10168c2ecf20Sopenharmony_ci snd_card_free(card); 10178c2ecf20Sopenharmony_ci return error; 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci if ((error = snd_opti9xx_probe(card)) < 0) { 10208c2ecf20Sopenharmony_ci snd_card_free(card); 10218c2ecf20Sopenharmony_ci return error; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci dev_set_drvdata(devptr, card); 10248c2ecf20Sopenharmony_ci return 0; 10258c2ecf20Sopenharmony_ci} 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_cistatic int snd_opti9xx_isa_remove(struct device *devptr, 10288c2ecf20Sopenharmony_ci unsigned int dev) 10298c2ecf20Sopenharmony_ci{ 10308c2ecf20Sopenharmony_ci snd_card_free(dev_get_drvdata(devptr)); 10318c2ecf20Sopenharmony_ci return 0; 10328c2ecf20Sopenharmony_ci} 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 10358c2ecf20Sopenharmony_cistatic int snd_opti9xx_suspend(struct snd_card *card) 10368c2ecf20Sopenharmony_ci{ 10378c2ecf20Sopenharmony_ci struct snd_opti9xx *chip = card->private_data; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 10408c2ecf20Sopenharmony_ci chip->codec->suspend(chip->codec); 10418c2ecf20Sopenharmony_ci return 0; 10428c2ecf20Sopenharmony_ci} 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_cistatic int snd_opti9xx_resume(struct snd_card *card) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci struct snd_opti9xx *chip = card->private_data; 10478c2ecf20Sopenharmony_ci int error, xdma2; 10488c2ecf20Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 10498c2ecf20Sopenharmony_ci xdma2 = dma2; 10508c2ecf20Sopenharmony_ci#else 10518c2ecf20Sopenharmony_ci xdma2 = -1; 10528c2ecf20Sopenharmony_ci#endif 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2, 10558c2ecf20Sopenharmony_ci mpu_port, mpu_irq); 10568c2ecf20Sopenharmony_ci if (error) 10578c2ecf20Sopenharmony_ci return error; 10588c2ecf20Sopenharmony_ci chip->codec->resume(chip->codec); 10598c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 10608c2ecf20Sopenharmony_ci return 0; 10618c2ecf20Sopenharmony_ci} 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic int snd_opti9xx_isa_suspend(struct device *dev, unsigned int n, 10648c2ecf20Sopenharmony_ci pm_message_t state) 10658c2ecf20Sopenharmony_ci{ 10668c2ecf20Sopenharmony_ci return snd_opti9xx_suspend(dev_get_drvdata(dev)); 10678c2ecf20Sopenharmony_ci} 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_cistatic int snd_opti9xx_isa_resume(struct device *dev, unsigned int n) 10708c2ecf20Sopenharmony_ci{ 10718c2ecf20Sopenharmony_ci return snd_opti9xx_resume(dev_get_drvdata(dev)); 10728c2ecf20Sopenharmony_ci} 10738c2ecf20Sopenharmony_ci#endif 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic struct isa_driver snd_opti9xx_driver = { 10768c2ecf20Sopenharmony_ci .match = snd_opti9xx_isa_match, 10778c2ecf20Sopenharmony_ci .probe = snd_opti9xx_isa_probe, 10788c2ecf20Sopenharmony_ci .remove = snd_opti9xx_isa_remove, 10798c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 10808c2ecf20Sopenharmony_ci .suspend = snd_opti9xx_isa_suspend, 10818c2ecf20Sopenharmony_ci .resume = snd_opti9xx_isa_resume, 10828c2ecf20Sopenharmony_ci#endif 10838c2ecf20Sopenharmony_ci .driver = { 10848c2ecf20Sopenharmony_ci .name = DEV_NAME 10858c2ecf20Sopenharmony_ci }, 10868c2ecf20Sopenharmony_ci}; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 10898c2ecf20Sopenharmony_cistatic int snd_opti9xx_pnp_probe(struct pnp_card_link *pcard, 10908c2ecf20Sopenharmony_ci const struct pnp_card_device_id *pid) 10918c2ecf20Sopenharmony_ci{ 10928c2ecf20Sopenharmony_ci struct snd_card *card; 10938c2ecf20Sopenharmony_ci int error, hw; 10948c2ecf20Sopenharmony_ci struct snd_opti9xx *chip; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci if (snd_opti9xx_pnp_is_probed) 10978c2ecf20Sopenharmony_ci return -EBUSY; 10988c2ecf20Sopenharmony_ci if (! isapnp) 10998c2ecf20Sopenharmony_ci return -ENODEV; 11008c2ecf20Sopenharmony_ci error = snd_opti9xx_card_new(&pcard->card->dev, &card); 11018c2ecf20Sopenharmony_ci if (error < 0) 11028c2ecf20Sopenharmony_ci return error; 11038c2ecf20Sopenharmony_ci chip = card->private_data; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci hw = snd_card_opti9xx_pnp(chip, pcard, pid); 11068c2ecf20Sopenharmony_ci switch (hw) { 11078c2ecf20Sopenharmony_ci case 0x0924: 11088c2ecf20Sopenharmony_ci hw = OPTi9XX_HW_82C924; 11098c2ecf20Sopenharmony_ci break; 11108c2ecf20Sopenharmony_ci case 0x0925: 11118c2ecf20Sopenharmony_ci hw = OPTi9XX_HW_82C925; 11128c2ecf20Sopenharmony_ci break; 11138c2ecf20Sopenharmony_ci case 0x0931: 11148c2ecf20Sopenharmony_ci hw = OPTi9XX_HW_82C931; 11158c2ecf20Sopenharmony_ci break; 11168c2ecf20Sopenharmony_ci default: 11178c2ecf20Sopenharmony_ci snd_card_free(card); 11188c2ecf20Sopenharmony_ci return -ENODEV; 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if ((error = snd_opti9xx_init(chip, hw))) { 11228c2ecf20Sopenharmony_ci snd_card_free(card); 11238c2ecf20Sopenharmony_ci return error; 11248c2ecf20Sopenharmony_ci } 11258c2ecf20Sopenharmony_ci error = snd_opti9xx_read_check(chip); 11268c2ecf20Sopenharmony_ci if (error) { 11278c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "OPTI chip not found\n"); 11288c2ecf20Sopenharmony_ci snd_card_free(card); 11298c2ecf20Sopenharmony_ci return error; 11308c2ecf20Sopenharmony_ci } 11318c2ecf20Sopenharmony_ci if ((error = snd_opti9xx_probe(card)) < 0) { 11328c2ecf20Sopenharmony_ci snd_card_free(card); 11338c2ecf20Sopenharmony_ci return error; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci pnp_set_card_drvdata(pcard, card); 11368c2ecf20Sopenharmony_ci snd_opti9xx_pnp_is_probed = 1; 11378c2ecf20Sopenharmony_ci return 0; 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cistatic void snd_opti9xx_pnp_remove(struct pnp_card_link *pcard) 11418c2ecf20Sopenharmony_ci{ 11428c2ecf20Sopenharmony_ci snd_card_free(pnp_get_card_drvdata(pcard)); 11438c2ecf20Sopenharmony_ci pnp_set_card_drvdata(pcard, NULL); 11448c2ecf20Sopenharmony_ci snd_opti9xx_pnp_is_probed = 0; 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 11488c2ecf20Sopenharmony_cistatic int snd_opti9xx_pnp_suspend(struct pnp_card_link *pcard, 11498c2ecf20Sopenharmony_ci pm_message_t state) 11508c2ecf20Sopenharmony_ci{ 11518c2ecf20Sopenharmony_ci return snd_opti9xx_suspend(pnp_get_card_drvdata(pcard)); 11528c2ecf20Sopenharmony_ci} 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_cistatic int snd_opti9xx_pnp_resume(struct pnp_card_link *pcard) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci return snd_opti9xx_resume(pnp_get_card_drvdata(pcard)); 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci#endif 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_cistatic struct pnp_card_driver opti9xx_pnpc_driver = { 11618c2ecf20Sopenharmony_ci .flags = PNP_DRIVER_RES_DISABLE, 11628c2ecf20Sopenharmony_ci .name = DEV_NAME, 11638c2ecf20Sopenharmony_ci .id_table = snd_opti9xx_pnpids, 11648c2ecf20Sopenharmony_ci .probe = snd_opti9xx_pnp_probe, 11658c2ecf20Sopenharmony_ci .remove = snd_opti9xx_pnp_remove, 11668c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 11678c2ecf20Sopenharmony_ci .suspend = snd_opti9xx_pnp_suspend, 11688c2ecf20Sopenharmony_ci .resume = snd_opti9xx_pnp_resume, 11698c2ecf20Sopenharmony_ci#endif 11708c2ecf20Sopenharmony_ci}; 11718c2ecf20Sopenharmony_ci#endif 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci#ifdef OPTi93X 11748c2ecf20Sopenharmony_ci#define CHIP_NAME "82C93x" 11758c2ecf20Sopenharmony_ci#else 11768c2ecf20Sopenharmony_ci#define CHIP_NAME "82C92x" 11778c2ecf20Sopenharmony_ci#endif 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cistatic int __init alsa_card_opti9xx_init(void) 11808c2ecf20Sopenharmony_ci{ 11818c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 11828c2ecf20Sopenharmony_ci pnp_register_card_driver(&opti9xx_pnpc_driver); 11838c2ecf20Sopenharmony_ci if (snd_opti9xx_pnp_is_probed) 11848c2ecf20Sopenharmony_ci return 0; 11858c2ecf20Sopenharmony_ci pnp_unregister_card_driver(&opti9xx_pnpc_driver); 11868c2ecf20Sopenharmony_ci#endif 11878c2ecf20Sopenharmony_ci return isa_register_driver(&snd_opti9xx_driver, 1); 11888c2ecf20Sopenharmony_ci} 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_cistatic void __exit alsa_card_opti9xx_exit(void) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci if (!snd_opti9xx_pnp_is_probed) { 11938c2ecf20Sopenharmony_ci isa_unregister_driver(&snd_opti9xx_driver); 11948c2ecf20Sopenharmony_ci return; 11958c2ecf20Sopenharmony_ci } 11968c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 11978c2ecf20Sopenharmony_ci pnp_unregister_card_driver(&opti9xx_pnpc_driver); 11988c2ecf20Sopenharmony_ci#endif 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_cimodule_init(alsa_card_opti9xx_init) 12028c2ecf20Sopenharmony_cimodule_exit(alsa_card_opti9xx_exit) 1203