162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards. 462306a36Sopenharmony_ci Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci Part of this code was developed at the Italian Ministry of Air Defence, 762306a36Sopenharmony_ci Sixth Division (oh, che pace ...), Rome. 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci Thanks to Maria Grazia Pollarini, Salvatore Vassallo. 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci*/ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/isa.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/pnp.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/io.h> 2162306a36Sopenharmony_ci#include <asm/dma.h> 2262306a36Sopenharmony_ci#include <sound/core.h> 2362306a36Sopenharmony_ci#include <sound/tlv.h> 2462306a36Sopenharmony_ci#include <sound/wss.h> 2562306a36Sopenharmony_ci#include <sound/mpu401.h> 2662306a36Sopenharmony_ci#include <sound/opl3.h> 2762306a36Sopenharmony_ci#ifndef OPTi93X 2862306a36Sopenharmony_ci#include <sound/opl4.h> 2962306a36Sopenharmony_ci#endif 3062306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IOPORT 3162306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IRQ 3262306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_DMA 3362306a36Sopenharmony_ci#include <sound/initval.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ciMODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>"); 3662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3762306a36Sopenharmony_ci#ifdef OPTi93X 3862306a36Sopenharmony_ciMODULE_DESCRIPTION("OPTi93X"); 3962306a36Sopenharmony_ci#else /* OPTi93X */ 4062306a36Sopenharmony_ci#ifdef CS4231 4162306a36Sopenharmony_ciMODULE_DESCRIPTION("OPTi92X - CS4231"); 4262306a36Sopenharmony_ci#else /* CS4231 */ 4362306a36Sopenharmony_ciMODULE_DESCRIPTION("OPTi92X - AD1848"); 4462306a36Sopenharmony_ci#endif /* CS4231 */ 4562306a36Sopenharmony_ci#endif /* OPTi93X */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ 4862306a36Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ 4962306a36Sopenharmony_ci//static bool enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */ 5062306a36Sopenharmony_ci#ifdef CONFIG_PNP 5162306a36Sopenharmony_cistatic bool isapnp = true; /* Enable ISA PnP detection */ 5262306a36Sopenharmony_ci#endif 5362306a36Sopenharmony_cistatic long port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */ 5462306a36Sopenharmony_cistatic long mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */ 5562306a36Sopenharmony_cistatic long fm_port = SNDRV_DEFAULT_PORT1; /* 0x388 */ 5662306a36Sopenharmony_cistatic int irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10,11 */ 5762306a36Sopenharmony_cistatic int mpu_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10 */ 5862306a36Sopenharmony_cistatic int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ 5962306a36Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 6062306a36Sopenharmony_cistatic int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ 6162306a36Sopenharmony_ci#endif /* CS4231 || OPTi93X */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cimodule_param(index, int, 0444); 6462306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for opti9xx based soundcard."); 6562306a36Sopenharmony_cimodule_param(id, charp, 0444); 6662306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for opti9xx based soundcard."); 6762306a36Sopenharmony_ci//module_param(enable, bool, 0444); 6862306a36Sopenharmony_ci//MODULE_PARM_DESC(enable, "Enable opti9xx soundcard."); 6962306a36Sopenharmony_ci#ifdef CONFIG_PNP 7062306a36Sopenharmony_cimodule_param(isapnp, bool, 0444); 7162306a36Sopenharmony_ciMODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard."); 7262306a36Sopenharmony_ci#endif 7362306a36Sopenharmony_cimodule_param_hw(port, long, ioport, 0444); 7462306a36Sopenharmony_ciMODULE_PARM_DESC(port, "WSS port # for opti9xx driver."); 7562306a36Sopenharmony_cimodule_param_hw(mpu_port, long, ioport, 0444); 7662306a36Sopenharmony_ciMODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver."); 7762306a36Sopenharmony_cimodule_param_hw(fm_port, long, ioport, 0444); 7862306a36Sopenharmony_ciMODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver."); 7962306a36Sopenharmony_cimodule_param_hw(irq, int, irq, 0444); 8062306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver."); 8162306a36Sopenharmony_cimodule_param_hw(mpu_irq, int, irq, 0444); 8262306a36Sopenharmony_ciMODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver."); 8362306a36Sopenharmony_cimodule_param_hw(dma1, int, dma, 0444); 8462306a36Sopenharmony_ciMODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver."); 8562306a36Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 8662306a36Sopenharmony_cimodule_param_hw(dma2, int, dma, 0444); 8762306a36Sopenharmony_ciMODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver."); 8862306a36Sopenharmony_ci#endif /* CS4231 || OPTi93X */ 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define OPTi9XX_HW_82C928 1 9162306a36Sopenharmony_ci#define OPTi9XX_HW_82C929 2 9262306a36Sopenharmony_ci#define OPTi9XX_HW_82C924 3 9362306a36Sopenharmony_ci#define OPTi9XX_HW_82C925 4 9462306a36Sopenharmony_ci#define OPTi9XX_HW_82C930 5 9562306a36Sopenharmony_ci#define OPTi9XX_HW_82C931 6 9662306a36Sopenharmony_ci#define OPTi9XX_HW_82C933 7 9762306a36Sopenharmony_ci#define OPTi9XX_HW_LAST OPTi9XX_HW_82C933 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define OPTi9XX_MC_REG(n) n 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#ifdef OPTi93X 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define OPTi93X_STATUS 0x02 10462306a36Sopenharmony_ci#define OPTi93X_PORT(chip, r) ((chip)->port + OPTi93X_##r) 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define OPTi93X_IRQ_PLAYBACK 0x04 10762306a36Sopenharmony_ci#define OPTi93X_IRQ_CAPTURE 0x08 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#endif /* OPTi93X */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistruct snd_opti9xx { 11262306a36Sopenharmony_ci unsigned short hardware; 11362306a36Sopenharmony_ci unsigned char password; 11462306a36Sopenharmony_ci char name[7]; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci unsigned long mc_base; 11762306a36Sopenharmony_ci struct resource *res_mc_base; 11862306a36Sopenharmony_ci unsigned long mc_base_size; 11962306a36Sopenharmony_ci#ifdef OPTi93X 12062306a36Sopenharmony_ci unsigned long mc_indir_index; 12162306a36Sopenharmony_ci struct resource *res_mc_indir; 12262306a36Sopenharmony_ci#endif /* OPTi93X */ 12362306a36Sopenharmony_ci struct snd_wss *codec; 12462306a36Sopenharmony_ci unsigned long pwd_reg; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci spinlock_t lock; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci long wss_base; 12962306a36Sopenharmony_ci int irq; 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic int snd_opti9xx_pnp_is_probed; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci#ifdef CONFIG_PNP 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic const struct pnp_card_device_id snd_opti9xx_pnpids[] = { 13762306a36Sopenharmony_ci#ifndef OPTi93X 13862306a36Sopenharmony_ci /* OPTi 82C924 */ 13962306a36Sopenharmony_ci { .id = "OPT0924", 14062306a36Sopenharmony_ci .devs = { { "OPT0000" }, { "OPT0002" }, { "OPT0005" } }, 14162306a36Sopenharmony_ci .driver_data = 0x0924 }, 14262306a36Sopenharmony_ci /* OPTi 82C925 */ 14362306a36Sopenharmony_ci { .id = "OPT0925", 14462306a36Sopenharmony_ci .devs = { { "OPT9250" }, { "OPT0002" }, { "OPT0005" } }, 14562306a36Sopenharmony_ci .driver_data = 0x0925 }, 14662306a36Sopenharmony_ci#else 14762306a36Sopenharmony_ci /* OPTi 82C931/3 */ 14862306a36Sopenharmony_ci { .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } }, 14962306a36Sopenharmony_ci .driver_data = 0x0931 }, 15062306a36Sopenharmony_ci#endif /* OPTi93X */ 15162306a36Sopenharmony_ci { .id = "" } 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#define DEV_NAME KBUILD_MODNAME 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic const char * const snd_opti9xx_names[] = { 16162306a36Sopenharmony_ci "unknown", 16262306a36Sopenharmony_ci "82C928", "82C929", 16362306a36Sopenharmony_ci "82C924", "82C925", 16462306a36Sopenharmony_ci "82C930", "82C931", "82C933" 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int snd_opti9xx_init(struct snd_opti9xx *chip, 16862306a36Sopenharmony_ci unsigned short hardware) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci chip->hardware = hardware; 17362306a36Sopenharmony_ci strcpy(chip->name, snd_opti9xx_names[hardware]); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci spin_lock_init(&chip->lock); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci chip->irq = -1; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci#ifndef OPTi93X 18062306a36Sopenharmony_ci#ifdef CONFIG_PNP 18162306a36Sopenharmony_ci if (isapnp && chip->mc_base) 18262306a36Sopenharmony_ci /* PnP resource gives the least 10 bits */ 18362306a36Sopenharmony_ci chip->mc_base |= 0xc00; 18462306a36Sopenharmony_ci else 18562306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 18662306a36Sopenharmony_ci { 18762306a36Sopenharmony_ci chip->mc_base = 0xf8c; 18862306a36Sopenharmony_ci chip->mc_base_size = opti9xx_mc_size[hardware]; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci#else 19162306a36Sopenharmony_ci chip->mc_base_size = opti9xx_mc_size[hardware]; 19262306a36Sopenharmony_ci#endif 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci switch (hardware) { 19562306a36Sopenharmony_ci#ifndef OPTi93X 19662306a36Sopenharmony_ci case OPTi9XX_HW_82C928: 19762306a36Sopenharmony_ci case OPTi9XX_HW_82C929: 19862306a36Sopenharmony_ci chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3; 19962306a36Sopenharmony_ci chip->pwd_reg = 3; 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci case OPTi9XX_HW_82C924: 20362306a36Sopenharmony_ci case OPTi9XX_HW_82C925: 20462306a36Sopenharmony_ci chip->password = 0xe5; 20562306a36Sopenharmony_ci chip->pwd_reg = 3; 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci#else /* OPTi93X */ 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci case OPTi9XX_HW_82C930: 21062306a36Sopenharmony_ci case OPTi9XX_HW_82C931: 21162306a36Sopenharmony_ci case OPTi9XX_HW_82C933: 21262306a36Sopenharmony_ci chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d; 21362306a36Sopenharmony_ci if (!chip->mc_indir_index) 21462306a36Sopenharmony_ci chip->mc_indir_index = 0xe0e; 21562306a36Sopenharmony_ci chip->password = 0xe4; 21662306a36Sopenharmony_ci chip->pwd_reg = 0; 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci#endif /* OPTi93X */ 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci default: 22162306a36Sopenharmony_ci snd_printk(KERN_ERR "chip %d not supported\n", hardware); 22262306a36Sopenharmony_ci return -ENODEV; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic unsigned char snd_opti9xx_read(struct snd_opti9xx *chip, 22862306a36Sopenharmony_ci unsigned char reg) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci unsigned long flags; 23162306a36Sopenharmony_ci unsigned char retval = 0xff; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 23462306a36Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci switch (chip->hardware) { 23762306a36Sopenharmony_ci#ifndef OPTi93X 23862306a36Sopenharmony_ci case OPTi9XX_HW_82C924: 23962306a36Sopenharmony_ci case OPTi9XX_HW_82C925: 24062306a36Sopenharmony_ci if (reg > 7) { 24162306a36Sopenharmony_ci outb(reg, chip->mc_base + 8); 24262306a36Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 24362306a36Sopenharmony_ci retval = inb(chip->mc_base + 9); 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci fallthrough; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci case OPTi9XX_HW_82C928: 24962306a36Sopenharmony_ci case OPTi9XX_HW_82C929: 25062306a36Sopenharmony_ci retval = inb(chip->mc_base + reg); 25162306a36Sopenharmony_ci break; 25262306a36Sopenharmony_ci#else /* OPTi93X */ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci case OPTi9XX_HW_82C930: 25562306a36Sopenharmony_ci case OPTi9XX_HW_82C931: 25662306a36Sopenharmony_ci case OPTi9XX_HW_82C933: 25762306a36Sopenharmony_ci outb(reg, chip->mc_indir_index); 25862306a36Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 25962306a36Sopenharmony_ci retval = inb(chip->mc_indir_index + 1); 26062306a36Sopenharmony_ci break; 26162306a36Sopenharmony_ci#endif /* OPTi93X */ 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci default: 26462306a36Sopenharmony_ci snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 26862306a36Sopenharmony_ci return retval; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg, 27262306a36Sopenharmony_ci unsigned char value) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci unsigned long flags; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 27762306a36Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci switch (chip->hardware) { 28062306a36Sopenharmony_ci#ifndef OPTi93X 28162306a36Sopenharmony_ci case OPTi9XX_HW_82C924: 28262306a36Sopenharmony_ci case OPTi9XX_HW_82C925: 28362306a36Sopenharmony_ci if (reg > 7) { 28462306a36Sopenharmony_ci outb(reg, chip->mc_base + 8); 28562306a36Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 28662306a36Sopenharmony_ci outb(value, chip->mc_base + 9); 28762306a36Sopenharmony_ci break; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci fallthrough; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci case OPTi9XX_HW_82C928: 29262306a36Sopenharmony_ci case OPTi9XX_HW_82C929: 29362306a36Sopenharmony_ci outb(value, chip->mc_base + reg); 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci#else /* OPTi93X */ 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci case OPTi9XX_HW_82C930: 29862306a36Sopenharmony_ci case OPTi9XX_HW_82C931: 29962306a36Sopenharmony_ci case OPTi9XX_HW_82C933: 30062306a36Sopenharmony_ci outb(reg, chip->mc_indir_index); 30162306a36Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 30262306a36Sopenharmony_ci outb(value, chip->mc_indir_index + 1); 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci#endif /* OPTi93X */ 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci default: 30762306a36Sopenharmony_ci snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic inline void snd_opti9xx_write_mask(struct snd_opti9xx *chip, 31562306a36Sopenharmony_ci unsigned char reg, unsigned char value, unsigned char mask) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci unsigned char oldval = snd_opti9xx_read(chip, reg); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci snd_opti9xx_write(chip, reg, (oldval & ~mask) | (value & mask)); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int snd_opti9xx_configure(struct snd_opti9xx *chip, 32362306a36Sopenharmony_ci long port, 32462306a36Sopenharmony_ci int irq, int dma1, int dma2, 32562306a36Sopenharmony_ci long mpu_port, int mpu_irq) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci unsigned char wss_base_bits; 32862306a36Sopenharmony_ci unsigned char irq_bits; 32962306a36Sopenharmony_ci unsigned char dma_bits; 33062306a36Sopenharmony_ci unsigned char mpu_port_bits = 0; 33162306a36Sopenharmony_ci unsigned char mpu_irq_bits; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci switch (chip->hardware) { 33462306a36Sopenharmony_ci#ifndef OPTi93X 33562306a36Sopenharmony_ci case OPTi9XX_HW_82C924: 33662306a36Sopenharmony_ci /* opti 929 mode (?), OPL3 clock output, audio enable */ 33762306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc); 33862306a36Sopenharmony_ci /* enable wave audio */ 33962306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02); 34062306a36Sopenharmony_ci fallthrough; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci case OPTi9XX_HW_82C925: 34362306a36Sopenharmony_ci /* enable WSS mode */ 34462306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); 34562306a36Sopenharmony_ci /* OPL3 FM synthesis */ 34662306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); 34762306a36Sopenharmony_ci /* disable Sound Blaster IRQ and DMA */ 34862306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff); 34962306a36Sopenharmony_ci#ifdef CS4231 35062306a36Sopenharmony_ci /* cs4231/4248 fix enabled */ 35162306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); 35262306a36Sopenharmony_ci#else 35362306a36Sopenharmony_ci /* cs4231/4248 fix disabled */ 35462306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); 35562306a36Sopenharmony_ci#endif /* CS4231 */ 35662306a36Sopenharmony_ci break; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci case OPTi9XX_HW_82C928: 35962306a36Sopenharmony_ci case OPTi9XX_HW_82C929: 36062306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); 36162306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20); 36262306a36Sopenharmony_ci /* 36362306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae); 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c); 36662306a36Sopenharmony_ci#ifdef CS4231 36762306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); 36862306a36Sopenharmony_ci#else 36962306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02); 37062306a36Sopenharmony_ci#endif /* CS4231 */ 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci#else /* OPTi93X */ 37462306a36Sopenharmony_ci case OPTi9XX_HW_82C931: 37562306a36Sopenharmony_ci /* disable 3D sound (set GPIO1 as output, low) */ 37662306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(20), 0x04, 0x0c); 37762306a36Sopenharmony_ci fallthrough; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci case OPTi9XX_HW_82C933: 38062306a36Sopenharmony_ci /* 38162306a36Sopenharmony_ci * The BTC 1817DW has QS1000 wavetable which is connected 38262306a36Sopenharmony_ci * to the serial digital input of the OPTI931. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(21), 0x82, 0xff); 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * This bit sets OPTI931 to automaticaly select FM 38762306a36Sopenharmony_ci * or digital input signal. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01); 39062306a36Sopenharmony_ci fallthrough; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci case OPTi9XX_HW_82C930: 39362306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03); 39462306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff); 39562306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 | 39662306a36Sopenharmony_ci (chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04), 39762306a36Sopenharmony_ci 0x34); 39862306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf); 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci#endif /* OPTi93X */ 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci default: 40362306a36Sopenharmony_ci snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 40462306a36Sopenharmony_ci return -EINVAL; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* PnP resource says it decodes only 10 bits of address */ 40862306a36Sopenharmony_ci switch (port & 0x3ff) { 40962306a36Sopenharmony_ci case 0x130: 41062306a36Sopenharmony_ci chip->wss_base = 0x530; 41162306a36Sopenharmony_ci wss_base_bits = 0x00; 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci case 0x204: 41462306a36Sopenharmony_ci chip->wss_base = 0x604; 41562306a36Sopenharmony_ci wss_base_bits = 0x03; 41662306a36Sopenharmony_ci break; 41762306a36Sopenharmony_ci case 0x280: 41862306a36Sopenharmony_ci chip->wss_base = 0xe80; 41962306a36Sopenharmony_ci wss_base_bits = 0x01; 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci case 0x340: 42262306a36Sopenharmony_ci chip->wss_base = 0xf40; 42362306a36Sopenharmony_ci wss_base_bits = 0x02; 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci default: 42662306a36Sopenharmony_ci snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", port); 42762306a36Sopenharmony_ci goto __skip_base; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci__skip_base: 43262306a36Sopenharmony_ci switch (irq) { 43362306a36Sopenharmony_ci//#ifdef OPTi93X 43462306a36Sopenharmony_ci case 5: 43562306a36Sopenharmony_ci irq_bits = 0x05; 43662306a36Sopenharmony_ci break; 43762306a36Sopenharmony_ci//#endif /* OPTi93X */ 43862306a36Sopenharmony_ci case 7: 43962306a36Sopenharmony_ci irq_bits = 0x01; 44062306a36Sopenharmony_ci break; 44162306a36Sopenharmony_ci case 9: 44262306a36Sopenharmony_ci irq_bits = 0x02; 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci case 10: 44562306a36Sopenharmony_ci irq_bits = 0x03; 44662306a36Sopenharmony_ci break; 44762306a36Sopenharmony_ci case 11: 44862306a36Sopenharmony_ci irq_bits = 0x04; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci default: 45162306a36Sopenharmony_ci snd_printk(KERN_WARNING "WSS irq # %d not valid\n", irq); 45262306a36Sopenharmony_ci goto __skip_resources; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci switch (dma1) { 45662306a36Sopenharmony_ci case 0: 45762306a36Sopenharmony_ci dma_bits = 0x01; 45862306a36Sopenharmony_ci break; 45962306a36Sopenharmony_ci case 1: 46062306a36Sopenharmony_ci dma_bits = 0x02; 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci case 3: 46362306a36Sopenharmony_ci dma_bits = 0x03; 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci default: 46662306a36Sopenharmony_ci snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n", dma1); 46762306a36Sopenharmony_ci goto __skip_resources; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 47162306a36Sopenharmony_ci if (dma1 == dma2) { 47262306a36Sopenharmony_ci snd_printk(KERN_ERR "don't want to share dmas\n"); 47362306a36Sopenharmony_ci return -EBUSY; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci switch (dma2) { 47762306a36Sopenharmony_ci case 0: 47862306a36Sopenharmony_ci case 1: 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci default: 48162306a36Sopenharmony_ci snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n", dma2); 48262306a36Sopenharmony_ci goto __skip_resources; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci dma_bits |= 0x04; 48562306a36Sopenharmony_ci#endif /* CS4231 || OPTi93X */ 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci#ifndef OPTi93X 48862306a36Sopenharmony_ci outb(irq_bits << 3 | dma_bits, chip->wss_base); 48962306a36Sopenharmony_ci#else /* OPTi93X */ 49062306a36Sopenharmony_ci snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits)); 49162306a36Sopenharmony_ci#endif /* OPTi93X */ 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci__skip_resources: 49462306a36Sopenharmony_ci if (chip->hardware > OPTi9XX_HW_82C928) { 49562306a36Sopenharmony_ci switch (mpu_port) { 49662306a36Sopenharmony_ci case 0: 49762306a36Sopenharmony_ci case -1: 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci case 0x300: 50062306a36Sopenharmony_ci mpu_port_bits = 0x03; 50162306a36Sopenharmony_ci break; 50262306a36Sopenharmony_ci case 0x310: 50362306a36Sopenharmony_ci mpu_port_bits = 0x02; 50462306a36Sopenharmony_ci break; 50562306a36Sopenharmony_ci case 0x320: 50662306a36Sopenharmony_ci mpu_port_bits = 0x01; 50762306a36Sopenharmony_ci break; 50862306a36Sopenharmony_ci case 0x330: 50962306a36Sopenharmony_ci mpu_port_bits = 0x00; 51062306a36Sopenharmony_ci break; 51162306a36Sopenharmony_ci default: 51262306a36Sopenharmony_ci snd_printk(KERN_WARNING 51362306a36Sopenharmony_ci "MPU-401 port 0x%lx not valid\n", mpu_port); 51462306a36Sopenharmony_ci goto __skip_mpu; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci switch (mpu_irq) { 51862306a36Sopenharmony_ci case 5: 51962306a36Sopenharmony_ci mpu_irq_bits = 0x02; 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci case 7: 52262306a36Sopenharmony_ci mpu_irq_bits = 0x03; 52362306a36Sopenharmony_ci break; 52462306a36Sopenharmony_ci case 9: 52562306a36Sopenharmony_ci mpu_irq_bits = 0x00; 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci case 10: 52862306a36Sopenharmony_ci mpu_irq_bits = 0x01; 52962306a36Sopenharmony_ci break; 53062306a36Sopenharmony_ci default: 53162306a36Sopenharmony_ci snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n", 53262306a36Sopenharmony_ci mpu_irq); 53362306a36Sopenharmony_ci goto __skip_mpu; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 53762306a36Sopenharmony_ci (mpu_port <= 0) ? 0x00 : 53862306a36Sopenharmony_ci 0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3, 53962306a36Sopenharmony_ci 0xf8); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci__skip_mpu: 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci#ifdef OPTi93X 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_step, -9300, 300, 0); 54962306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0); 55062306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_4bit_12db_max, -3300, 300, 0); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_opti93x_controls[] = { 55362306a36Sopenharmony_ciWSS_DOUBLE("Master Playback Switch", 0, 55462306a36Sopenharmony_ci OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1), 55562306a36Sopenharmony_ciWSS_DOUBLE_TLV("Master Playback Volume", 0, 55662306a36Sopenharmony_ci OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1, 55762306a36Sopenharmony_ci db_scale_5bit_3db_step), 55862306a36Sopenharmony_ciWSS_DOUBLE_TLV("PCM Playback Volume", 0, 55962306a36Sopenharmony_ci CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1, 56062306a36Sopenharmony_ci db_scale_5bit), 56162306a36Sopenharmony_ciWSS_DOUBLE_TLV("FM Playback Volume", 0, 56262306a36Sopenharmony_ci CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1, 56362306a36Sopenharmony_ci db_scale_4bit_12db_max), 56462306a36Sopenharmony_ciWSS_DOUBLE("Line Playback Switch", 0, 56562306a36Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), 56662306a36Sopenharmony_ciWSS_DOUBLE_TLV("Line Playback Volume", 0, 56762306a36Sopenharmony_ci CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1, 56862306a36Sopenharmony_ci db_scale_4bit_12db_max), 56962306a36Sopenharmony_ciWSS_DOUBLE("Mic Playback Switch", 0, 57062306a36Sopenharmony_ci OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1), 57162306a36Sopenharmony_ciWSS_DOUBLE_TLV("Mic Playback Volume", 0, 57262306a36Sopenharmony_ci OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1, 57362306a36Sopenharmony_ci db_scale_4bit_12db_max), 57462306a36Sopenharmony_ciWSS_DOUBLE_TLV("CD Playback Volume", 0, 57562306a36Sopenharmony_ci CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1, 57662306a36Sopenharmony_ci db_scale_4bit_12db_max), 57762306a36Sopenharmony_ciWSS_DOUBLE("Aux Playback Switch", 0, 57862306a36Sopenharmony_ci OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1), 57962306a36Sopenharmony_ciWSS_DOUBLE_TLV("Aux Playback Volume", 0, 58062306a36Sopenharmony_ci OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1, 58162306a36Sopenharmony_ci db_scale_4bit_12db_max), 58262306a36Sopenharmony_ci}; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic int snd_opti93x_mixer(struct snd_wss *chip) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct snd_card *card; 58762306a36Sopenharmony_ci unsigned int idx; 58862306a36Sopenharmony_ci struct snd_ctl_elem_id id1, id2; 58962306a36Sopenharmony_ci int err; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (snd_BUG_ON(!chip || !chip->pcm)) 59262306a36Sopenharmony_ci return -EINVAL; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci card = chip->card; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci strcpy(card->mixername, chip->pcm->name); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci memset(&id1, 0, sizeof(id1)); 59962306a36Sopenharmony_ci memset(&id2, 0, sizeof(id2)); 60062306a36Sopenharmony_ci id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 60162306a36Sopenharmony_ci /* reassign AUX0 switch to CD */ 60262306a36Sopenharmony_ci strcpy(id1.name, "Aux Playback Switch"); 60362306a36Sopenharmony_ci strcpy(id2.name, "CD Playback Switch"); 60462306a36Sopenharmony_ci err = snd_ctl_rename_id(card, &id1, &id2); 60562306a36Sopenharmony_ci if (err < 0) { 60662306a36Sopenharmony_ci snd_printk(KERN_ERR "Cannot rename opti93x control\n"); 60762306a36Sopenharmony_ci return err; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci /* reassign AUX1 switch to FM */ 61062306a36Sopenharmony_ci strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; 61162306a36Sopenharmony_ci strcpy(id2.name, "FM Playback Switch"); 61262306a36Sopenharmony_ci err = snd_ctl_rename_id(card, &id1, &id2); 61362306a36Sopenharmony_ci if (err < 0) { 61462306a36Sopenharmony_ci snd_printk(KERN_ERR "Cannot rename opti93x control\n"); 61562306a36Sopenharmony_ci return err; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci /* remove AUX1 volume */ 61862306a36Sopenharmony_ci strcpy(id1.name, "Aux Playback Volume"); id1.index = 1; 61962306a36Sopenharmony_ci snd_ctl_remove_id(card, &id1); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* Replace WSS volume controls with OPTi93x volume controls */ 62262306a36Sopenharmony_ci id1.index = 0; 62362306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) { 62462306a36Sopenharmony_ci strcpy(id1.name, snd_opti93x_controls[idx].name); 62562306a36Sopenharmony_ci snd_ctl_remove_id(card, &id1); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci err = snd_ctl_add(card, 62862306a36Sopenharmony_ci snd_ctl_new1(&snd_opti93x_controls[idx], chip)); 62962306a36Sopenharmony_ci if (err < 0) 63062306a36Sopenharmony_ci return err; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci return 0; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci struct snd_opti9xx *chip = dev_id; 63862306a36Sopenharmony_ci struct snd_wss *codec = chip->codec; 63962306a36Sopenharmony_ci unsigned char status; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (!codec) 64262306a36Sopenharmony_ci return IRQ_HANDLED; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11)); 64562306a36Sopenharmony_ci if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream) 64662306a36Sopenharmony_ci snd_pcm_period_elapsed(codec->playback_substream); 64762306a36Sopenharmony_ci if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) { 64862306a36Sopenharmony_ci snd_wss_overrange(codec); 64962306a36Sopenharmony_ci snd_pcm_period_elapsed(codec->capture_substream); 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci outb(0x00, OPTi93X_PORT(codec, STATUS)); 65262306a36Sopenharmony_ci return IRQ_HANDLED; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci#endif /* OPTi93X */ 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic int snd_opti9xx_read_check(struct snd_card *card, 65862306a36Sopenharmony_ci struct snd_opti9xx *chip) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci unsigned char value; 66162306a36Sopenharmony_ci#ifdef OPTi93X 66262306a36Sopenharmony_ci unsigned long flags; 66362306a36Sopenharmony_ci#endif 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci chip->res_mc_base = 66662306a36Sopenharmony_ci devm_request_region(card->dev, chip->mc_base, 66762306a36Sopenharmony_ci chip->mc_base_size, "OPTi9xx MC"); 66862306a36Sopenharmony_ci if (!chip->res_mc_base) 66962306a36Sopenharmony_ci return -EBUSY; 67062306a36Sopenharmony_ci#ifndef OPTi93X 67162306a36Sopenharmony_ci value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)); 67262306a36Sopenharmony_ci if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1))) 67362306a36Sopenharmony_ci if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1))) 67462306a36Sopenharmony_ci return 0; 67562306a36Sopenharmony_ci#else /* OPTi93X */ 67662306a36Sopenharmony_ci chip->res_mc_indir = 67762306a36Sopenharmony_ci devm_request_region(card->dev, chip->mc_indir_index, 2, 67862306a36Sopenharmony_ci "OPTi93x MC"); 67962306a36Sopenharmony_ci if (!chip->res_mc_indir) 68062306a36Sopenharmony_ci return -EBUSY; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 68362306a36Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 68462306a36Sopenharmony_ci outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base); 68562306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)); 68862306a36Sopenharmony_ci snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value); 68962306a36Sopenharmony_ci if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value) 69062306a36Sopenharmony_ci return 0; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci devm_release_resource(card->dev, chip->res_mc_indir); 69362306a36Sopenharmony_ci chip->res_mc_indir = NULL; 69462306a36Sopenharmony_ci#endif /* OPTi93X */ 69562306a36Sopenharmony_ci devm_release_resource(card->dev, chip->res_mc_base); 69662306a36Sopenharmony_ci chip->res_mc_base = NULL; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci return -ENODEV; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic int snd_card_opti9xx_detect(struct snd_card *card, 70262306a36Sopenharmony_ci struct snd_opti9xx *chip) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci int i, err; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci#ifndef OPTi93X 70762306a36Sopenharmony_ci for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) { 70862306a36Sopenharmony_ci#else 70962306a36Sopenharmony_ci for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) { 71062306a36Sopenharmony_ci#endif 71162306a36Sopenharmony_ci err = snd_opti9xx_init(chip, i); 71262306a36Sopenharmony_ci if (err < 0) 71362306a36Sopenharmony_ci return err; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci err = snd_opti9xx_read_check(card, chip); 71662306a36Sopenharmony_ci if (err == 0) 71762306a36Sopenharmony_ci return 1; 71862306a36Sopenharmony_ci#ifdef OPTi93X 71962306a36Sopenharmony_ci chip->mc_indir_index = 0; 72062306a36Sopenharmony_ci#endif 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci return -ENODEV; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci#ifdef CONFIG_PNP 72662306a36Sopenharmony_cistatic int snd_card_opti9xx_pnp(struct snd_opti9xx *chip, 72762306a36Sopenharmony_ci struct pnp_card_link *card, 72862306a36Sopenharmony_ci const struct pnp_card_device_id *pid) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci struct pnp_dev *pdev; 73162306a36Sopenharmony_ci int err; 73262306a36Sopenharmony_ci struct pnp_dev *devmpu; 73362306a36Sopenharmony_ci#ifndef OPTi93X 73462306a36Sopenharmony_ci struct pnp_dev *devmc; 73562306a36Sopenharmony_ci#endif 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci pdev = pnp_request_card_device(card, pid->devs[0].id, NULL); 73862306a36Sopenharmony_ci if (pdev == NULL) 73962306a36Sopenharmony_ci return -EBUSY; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci err = pnp_activate_dev(pdev); 74262306a36Sopenharmony_ci if (err < 0) { 74362306a36Sopenharmony_ci snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err); 74462306a36Sopenharmony_ci return err; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci#ifdef OPTi93X 74862306a36Sopenharmony_ci port = pnp_port_start(pdev, 0) - 4; 74962306a36Sopenharmony_ci fm_port = pnp_port_start(pdev, 1) + 8; 75062306a36Sopenharmony_ci /* adjust mc_indir_index - some cards report it at 0xe?d, 75162306a36Sopenharmony_ci other at 0xe?c but it really is always at 0xe?e */ 75262306a36Sopenharmony_ci chip->mc_indir_index = (pnp_port_start(pdev, 3) & ~0xf) | 0xe; 75362306a36Sopenharmony_ci#else 75462306a36Sopenharmony_ci devmc = pnp_request_card_device(card, pid->devs[2].id, NULL); 75562306a36Sopenharmony_ci if (devmc == NULL) 75662306a36Sopenharmony_ci return -EBUSY; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci err = pnp_activate_dev(devmc); 75962306a36Sopenharmony_ci if (err < 0) { 76062306a36Sopenharmony_ci snd_printk(KERN_ERR "MC pnp configure failure: %d\n", err); 76162306a36Sopenharmony_ci return err; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci port = pnp_port_start(pdev, 1); 76562306a36Sopenharmony_ci fm_port = pnp_port_start(pdev, 2) + 8; 76662306a36Sopenharmony_ci /* 76762306a36Sopenharmony_ci * The MC(0) is never accessed and card does not 76862306a36Sopenharmony_ci * include it in the PnP resource range. OPTI93x include it. 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_ci chip->mc_base = pnp_port_start(devmc, 0) - 1; 77162306a36Sopenharmony_ci chip->mc_base_size = pnp_port_len(devmc, 0) + 1; 77262306a36Sopenharmony_ci#endif /* OPTi93X */ 77362306a36Sopenharmony_ci irq = pnp_irq(pdev, 0); 77462306a36Sopenharmony_ci dma1 = pnp_dma(pdev, 0); 77562306a36Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 77662306a36Sopenharmony_ci dma2 = pnp_dma(pdev, 1); 77762306a36Sopenharmony_ci#endif /* CS4231 || OPTi93X */ 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (devmpu && mpu_port > 0) { 78262306a36Sopenharmony_ci err = pnp_activate_dev(devmpu); 78362306a36Sopenharmony_ci if (err < 0) { 78462306a36Sopenharmony_ci snd_printk(KERN_ERR "MPU401 pnp configure failure\n"); 78562306a36Sopenharmony_ci mpu_port = -1; 78662306a36Sopenharmony_ci } else { 78762306a36Sopenharmony_ci mpu_port = pnp_port_start(devmpu, 0); 78862306a36Sopenharmony_ci mpu_irq = pnp_irq(devmpu, 0); 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci return pid->driver_data; 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic int snd_opti9xx_probe(struct snd_card *card) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci static const long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1}; 79862306a36Sopenharmony_ci int error; 79962306a36Sopenharmony_ci int xdma2; 80062306a36Sopenharmony_ci struct snd_opti9xx *chip = card->private_data; 80162306a36Sopenharmony_ci struct snd_wss *codec; 80262306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 80362306a36Sopenharmony_ci struct snd_hwdep *synth; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 80662306a36Sopenharmony_ci xdma2 = dma2; 80762306a36Sopenharmony_ci#else 80862306a36Sopenharmony_ci xdma2 = -1; 80962306a36Sopenharmony_ci#endif 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (port == SNDRV_AUTO_PORT) { 81262306a36Sopenharmony_ci port = snd_legacy_find_free_ioport(possible_ports, 4); 81362306a36Sopenharmony_ci if (port < 0) { 81462306a36Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free WSS port\n"); 81562306a36Sopenharmony_ci return -EBUSY; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2, 81962306a36Sopenharmony_ci mpu_port, mpu_irq); 82062306a36Sopenharmony_ci if (error) 82162306a36Sopenharmony_ci return error; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci error = snd_wss_create(card, chip->wss_base + 4, -1, irq, dma1, xdma2, 82462306a36Sopenharmony_ci#ifdef OPTi93X 82562306a36Sopenharmony_ci WSS_HW_OPTI93X, WSS_HWSHARE_IRQ, 82662306a36Sopenharmony_ci#else 82762306a36Sopenharmony_ci WSS_HW_DETECT, 0, 82862306a36Sopenharmony_ci#endif 82962306a36Sopenharmony_ci &codec); 83062306a36Sopenharmony_ci if (error < 0) 83162306a36Sopenharmony_ci return error; 83262306a36Sopenharmony_ci chip->codec = codec; 83362306a36Sopenharmony_ci error = snd_wss_pcm(codec, 0); 83462306a36Sopenharmony_ci if (error < 0) 83562306a36Sopenharmony_ci return error; 83662306a36Sopenharmony_ci error = snd_wss_mixer(codec); 83762306a36Sopenharmony_ci if (error < 0) 83862306a36Sopenharmony_ci return error; 83962306a36Sopenharmony_ci#ifdef OPTi93X 84062306a36Sopenharmony_ci error = snd_opti93x_mixer(codec); 84162306a36Sopenharmony_ci if (error < 0) 84262306a36Sopenharmony_ci return error; 84362306a36Sopenharmony_ci#endif 84462306a36Sopenharmony_ci#ifdef CS4231 84562306a36Sopenharmony_ci error = snd_wss_timer(codec, 0); 84662306a36Sopenharmony_ci if (error < 0) 84762306a36Sopenharmony_ci return error; 84862306a36Sopenharmony_ci#endif 84962306a36Sopenharmony_ci#ifdef OPTi93X 85062306a36Sopenharmony_ci error = devm_request_irq(card->dev, irq, snd_opti93x_interrupt, 85162306a36Sopenharmony_ci 0, DEV_NAME" - WSS", chip); 85262306a36Sopenharmony_ci if (error < 0) { 85362306a36Sopenharmony_ci snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", irq); 85462306a36Sopenharmony_ci return error; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci#endif 85762306a36Sopenharmony_ci chip->irq = irq; 85862306a36Sopenharmony_ci card->sync_irq = chip->irq; 85962306a36Sopenharmony_ci strcpy(card->driver, chip->name); 86062306a36Sopenharmony_ci sprintf(card->shortname, "OPTi %s", card->driver); 86162306a36Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 86262306a36Sopenharmony_ci scnprintf(card->longname, sizeof(card->longname), 86362306a36Sopenharmony_ci "%s, %s at 0x%lx, irq %d, dma %d&%d", 86462306a36Sopenharmony_ci card->shortname, codec->pcm->name, 86562306a36Sopenharmony_ci chip->wss_base + 4, irq, dma1, xdma2); 86662306a36Sopenharmony_ci#else 86762306a36Sopenharmony_ci scnprintf(card->longname, sizeof(card->longname), 86862306a36Sopenharmony_ci "%s, %s at 0x%lx, irq %d, dma %d", 86962306a36Sopenharmony_ci card->shortname, codec->pcm->name, chip->wss_base + 4, irq, 87062306a36Sopenharmony_ci dma1); 87162306a36Sopenharmony_ci#endif /* CS4231 || OPTi93X */ 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT) 87462306a36Sopenharmony_ci rmidi = NULL; 87562306a36Sopenharmony_ci else { 87662306a36Sopenharmony_ci error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, 87762306a36Sopenharmony_ci mpu_port, 0, mpu_irq, &rmidi); 87862306a36Sopenharmony_ci if (error) 87962306a36Sopenharmony_ci snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", 88062306a36Sopenharmony_ci mpu_port); 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) { 88462306a36Sopenharmony_ci struct snd_opl3 *opl3 = NULL; 88562306a36Sopenharmony_ci#ifndef OPTi93X 88662306a36Sopenharmony_ci if (chip->hardware == OPTi9XX_HW_82C928 || 88762306a36Sopenharmony_ci chip->hardware == OPTi9XX_HW_82C929 || 88862306a36Sopenharmony_ci chip->hardware == OPTi9XX_HW_82C924) { 88962306a36Sopenharmony_ci struct snd_opl4 *opl4; 89062306a36Sopenharmony_ci /* assume we have an OPL4 */ 89162306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 89262306a36Sopenharmony_ci 0x20, 0x20); 89362306a36Sopenharmony_ci if (snd_opl4_create(card, fm_port, fm_port - 8, 89462306a36Sopenharmony_ci 2, &opl3, &opl4) < 0) { 89562306a36Sopenharmony_ci /* no luck, use OPL3 instead */ 89662306a36Sopenharmony_ci snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 89762306a36Sopenharmony_ci 0x00, 0x20); 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci#endif /* !OPTi93X */ 90162306a36Sopenharmony_ci if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2, 90262306a36Sopenharmony_ci OPL3_HW_AUTO, 0, &opl3) < 0) { 90362306a36Sopenharmony_ci snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n", 90462306a36Sopenharmony_ci fm_port, fm_port + 4 - 1); 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci if (opl3) { 90762306a36Sopenharmony_ci error = snd_opl3_hwdep_new(opl3, 0, 1, &synth); 90862306a36Sopenharmony_ci if (error < 0) 90962306a36Sopenharmony_ci return error; 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci return snd_card_register(card); 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic int snd_opti9xx_card_new(struct device *pdev, struct snd_card **cardp) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci struct snd_card *card; 91962306a36Sopenharmony_ci int err; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci err = snd_devm_card_new(pdev, index, id, THIS_MODULE, 92262306a36Sopenharmony_ci sizeof(struct snd_opti9xx), &card); 92362306a36Sopenharmony_ci if (err < 0) 92462306a36Sopenharmony_ci return err; 92562306a36Sopenharmony_ci *cardp = card; 92662306a36Sopenharmony_ci return 0; 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_cistatic int snd_opti9xx_isa_match(struct device *devptr, 93062306a36Sopenharmony_ci unsigned int dev) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci#ifdef CONFIG_PNP 93362306a36Sopenharmony_ci if (snd_opti9xx_pnp_is_probed) 93462306a36Sopenharmony_ci return 0; 93562306a36Sopenharmony_ci if (isapnp) 93662306a36Sopenharmony_ci return 0; 93762306a36Sopenharmony_ci#endif 93862306a36Sopenharmony_ci return 1; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic int snd_opti9xx_isa_probe(struct device *devptr, 94262306a36Sopenharmony_ci unsigned int dev) 94362306a36Sopenharmony_ci{ 94462306a36Sopenharmony_ci struct snd_card *card; 94562306a36Sopenharmony_ci int error; 94662306a36Sopenharmony_ci static const long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1}; 94762306a36Sopenharmony_ci#ifdef OPTi93X 94862306a36Sopenharmony_ci static const int possible_irqs[] = {5, 9, 10, 11, 7, -1}; 94962306a36Sopenharmony_ci#else 95062306a36Sopenharmony_ci static const int possible_irqs[] = {9, 10, 11, 7, -1}; 95162306a36Sopenharmony_ci#endif /* OPTi93X */ 95262306a36Sopenharmony_ci static const int possible_mpu_irqs[] = {5, 9, 10, 7, -1}; 95362306a36Sopenharmony_ci static const int possible_dma1s[] = {3, 1, 0, -1}; 95462306a36Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 95562306a36Sopenharmony_ci static const int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}}; 95662306a36Sopenharmony_ci#endif /* CS4231 || OPTi93X */ 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (mpu_port == SNDRV_AUTO_PORT) { 95962306a36Sopenharmony_ci mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2); 96062306a36Sopenharmony_ci if (mpu_port < 0) { 96162306a36Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free MPU401 port\n"); 96262306a36Sopenharmony_ci return -EBUSY; 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci if (irq == SNDRV_AUTO_IRQ) { 96662306a36Sopenharmony_ci irq = snd_legacy_find_free_irq(possible_irqs); 96762306a36Sopenharmony_ci if (irq < 0) { 96862306a36Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free IRQ\n"); 96962306a36Sopenharmony_ci return -EBUSY; 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci if (mpu_irq == SNDRV_AUTO_IRQ) { 97362306a36Sopenharmony_ci mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs); 97462306a36Sopenharmony_ci if (mpu_irq < 0) { 97562306a36Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n"); 97662306a36Sopenharmony_ci return -EBUSY; 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci if (dma1 == SNDRV_AUTO_DMA) { 98062306a36Sopenharmony_ci dma1 = snd_legacy_find_free_dma(possible_dma1s); 98162306a36Sopenharmony_ci if (dma1 < 0) { 98262306a36Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free DMA1\n"); 98362306a36Sopenharmony_ci return -EBUSY; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 98762306a36Sopenharmony_ci if (dma2 == SNDRV_AUTO_DMA) { 98862306a36Sopenharmony_ci dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]); 98962306a36Sopenharmony_ci if (dma2 < 0) { 99062306a36Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free DMA2\n"); 99162306a36Sopenharmony_ci return -EBUSY; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci#endif 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci error = snd_opti9xx_card_new(devptr, &card); 99762306a36Sopenharmony_ci if (error < 0) 99862306a36Sopenharmony_ci return error; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci error = snd_card_opti9xx_detect(card, card->private_data); 100162306a36Sopenharmony_ci if (error < 0) 100262306a36Sopenharmony_ci return error; 100362306a36Sopenharmony_ci error = snd_opti9xx_probe(card); 100462306a36Sopenharmony_ci if (error < 0) 100562306a36Sopenharmony_ci return error; 100662306a36Sopenharmony_ci dev_set_drvdata(devptr, card); 100762306a36Sopenharmony_ci return 0; 100862306a36Sopenharmony_ci} 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci#ifdef CONFIG_PM 101162306a36Sopenharmony_cistatic int snd_opti9xx_suspend(struct snd_card *card) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci struct snd_opti9xx *chip = card->private_data; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 101662306a36Sopenharmony_ci chip->codec->suspend(chip->codec); 101762306a36Sopenharmony_ci return 0; 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic int snd_opti9xx_resume(struct snd_card *card) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci struct snd_opti9xx *chip = card->private_data; 102362306a36Sopenharmony_ci int error, xdma2; 102462306a36Sopenharmony_ci#if defined(CS4231) || defined(OPTi93X) 102562306a36Sopenharmony_ci xdma2 = dma2; 102662306a36Sopenharmony_ci#else 102762306a36Sopenharmony_ci xdma2 = -1; 102862306a36Sopenharmony_ci#endif 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2, 103162306a36Sopenharmony_ci mpu_port, mpu_irq); 103262306a36Sopenharmony_ci if (error) 103362306a36Sopenharmony_ci return error; 103462306a36Sopenharmony_ci chip->codec->resume(chip->codec); 103562306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 103662306a36Sopenharmony_ci return 0; 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_cistatic int snd_opti9xx_isa_suspend(struct device *dev, unsigned int n, 104062306a36Sopenharmony_ci pm_message_t state) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci return snd_opti9xx_suspend(dev_get_drvdata(dev)); 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic int snd_opti9xx_isa_resume(struct device *dev, unsigned int n) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci return snd_opti9xx_resume(dev_get_drvdata(dev)); 104862306a36Sopenharmony_ci} 104962306a36Sopenharmony_ci#endif 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cistatic struct isa_driver snd_opti9xx_driver = { 105262306a36Sopenharmony_ci .match = snd_opti9xx_isa_match, 105362306a36Sopenharmony_ci .probe = snd_opti9xx_isa_probe, 105462306a36Sopenharmony_ci#ifdef CONFIG_PM 105562306a36Sopenharmony_ci .suspend = snd_opti9xx_isa_suspend, 105662306a36Sopenharmony_ci .resume = snd_opti9xx_isa_resume, 105762306a36Sopenharmony_ci#endif 105862306a36Sopenharmony_ci .driver = { 105962306a36Sopenharmony_ci .name = DEV_NAME 106062306a36Sopenharmony_ci }, 106162306a36Sopenharmony_ci}; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci#ifdef CONFIG_PNP 106462306a36Sopenharmony_cistatic int snd_opti9xx_pnp_probe(struct pnp_card_link *pcard, 106562306a36Sopenharmony_ci const struct pnp_card_device_id *pid) 106662306a36Sopenharmony_ci{ 106762306a36Sopenharmony_ci struct snd_card *card; 106862306a36Sopenharmony_ci int error, hw; 106962306a36Sopenharmony_ci struct snd_opti9xx *chip; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (snd_opti9xx_pnp_is_probed) 107262306a36Sopenharmony_ci return -EBUSY; 107362306a36Sopenharmony_ci if (! isapnp) 107462306a36Sopenharmony_ci return -ENODEV; 107562306a36Sopenharmony_ci error = snd_opti9xx_card_new(&pcard->card->dev, &card); 107662306a36Sopenharmony_ci if (error < 0) 107762306a36Sopenharmony_ci return error; 107862306a36Sopenharmony_ci chip = card->private_data; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci hw = snd_card_opti9xx_pnp(chip, pcard, pid); 108162306a36Sopenharmony_ci switch (hw) { 108262306a36Sopenharmony_ci case 0x0924: 108362306a36Sopenharmony_ci hw = OPTi9XX_HW_82C924; 108462306a36Sopenharmony_ci break; 108562306a36Sopenharmony_ci case 0x0925: 108662306a36Sopenharmony_ci hw = OPTi9XX_HW_82C925; 108762306a36Sopenharmony_ci break; 108862306a36Sopenharmony_ci case 0x0931: 108962306a36Sopenharmony_ci hw = OPTi9XX_HW_82C931; 109062306a36Sopenharmony_ci break; 109162306a36Sopenharmony_ci default: 109262306a36Sopenharmony_ci return -ENODEV; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci error = snd_opti9xx_init(chip, hw); 109662306a36Sopenharmony_ci if (error) 109762306a36Sopenharmony_ci return error; 109862306a36Sopenharmony_ci error = snd_opti9xx_read_check(card, chip); 109962306a36Sopenharmony_ci if (error) { 110062306a36Sopenharmony_ci snd_printk(KERN_ERR "OPTI chip not found\n"); 110162306a36Sopenharmony_ci return error; 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci error = snd_opti9xx_probe(card); 110462306a36Sopenharmony_ci if (error < 0) 110562306a36Sopenharmony_ci return error; 110662306a36Sopenharmony_ci pnp_set_card_drvdata(pcard, card); 110762306a36Sopenharmony_ci snd_opti9xx_pnp_is_probed = 1; 110862306a36Sopenharmony_ci return 0; 110962306a36Sopenharmony_ci} 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_cistatic void snd_opti9xx_pnp_remove(struct pnp_card_link *pcard) 111262306a36Sopenharmony_ci{ 111362306a36Sopenharmony_ci snd_opti9xx_pnp_is_probed = 0; 111462306a36Sopenharmony_ci} 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci#ifdef CONFIG_PM 111762306a36Sopenharmony_cistatic int snd_opti9xx_pnp_suspend(struct pnp_card_link *pcard, 111862306a36Sopenharmony_ci pm_message_t state) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci return snd_opti9xx_suspend(pnp_get_card_drvdata(pcard)); 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_cistatic int snd_opti9xx_pnp_resume(struct pnp_card_link *pcard) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci return snd_opti9xx_resume(pnp_get_card_drvdata(pcard)); 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci#endif 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic struct pnp_card_driver opti9xx_pnpc_driver = { 113062306a36Sopenharmony_ci .flags = PNP_DRIVER_RES_DISABLE, 113162306a36Sopenharmony_ci .name = DEV_NAME, 113262306a36Sopenharmony_ci .id_table = snd_opti9xx_pnpids, 113362306a36Sopenharmony_ci .probe = snd_opti9xx_pnp_probe, 113462306a36Sopenharmony_ci .remove = snd_opti9xx_pnp_remove, 113562306a36Sopenharmony_ci#ifdef CONFIG_PM 113662306a36Sopenharmony_ci .suspend = snd_opti9xx_pnp_suspend, 113762306a36Sopenharmony_ci .resume = snd_opti9xx_pnp_resume, 113862306a36Sopenharmony_ci#endif 113962306a36Sopenharmony_ci}; 114062306a36Sopenharmony_ci#endif 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci#ifdef OPTi93X 114362306a36Sopenharmony_ci#define CHIP_NAME "82C93x" 114462306a36Sopenharmony_ci#else 114562306a36Sopenharmony_ci#define CHIP_NAME "82C92x" 114662306a36Sopenharmony_ci#endif 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_cistatic int __init alsa_card_opti9xx_init(void) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci#ifdef CONFIG_PNP 115162306a36Sopenharmony_ci pnp_register_card_driver(&opti9xx_pnpc_driver); 115262306a36Sopenharmony_ci if (snd_opti9xx_pnp_is_probed) 115362306a36Sopenharmony_ci return 0; 115462306a36Sopenharmony_ci pnp_unregister_card_driver(&opti9xx_pnpc_driver); 115562306a36Sopenharmony_ci#endif 115662306a36Sopenharmony_ci return isa_register_driver(&snd_opti9xx_driver, 1); 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic void __exit alsa_card_opti9xx_exit(void) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci if (!snd_opti9xx_pnp_is_probed) { 116262306a36Sopenharmony_ci isa_unregister_driver(&snd_opti9xx_driver); 116362306a36Sopenharmony_ci return; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci#ifdef CONFIG_PNP 116662306a36Sopenharmony_ci pnp_unregister_card_driver(&opti9xx_pnpc_driver); 116762306a36Sopenharmony_ci#endif 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_cimodule_init(alsa_card_opti9xx_init) 117162306a36Sopenharmony_cimodule_exit(alsa_card_opti9xx_exit) 1172