162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ALSA soundcard driver for Miro miroSOUND PCM1 pro 462306a36Sopenharmony_ci * miroSOUND PCM12 562306a36Sopenharmony_ci * miroSOUND PCM20 Radio 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2004-2005 Martin Langer <martin-langer@gmx.de> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Based on OSS ACI and ALSA OPTi9xx drivers 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/isa.h> 1562306a36Sopenharmony_ci#include <linux/pnp.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/ioport.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/io.h> 2062306a36Sopenharmony_ci#include <asm/dma.h> 2162306a36Sopenharmony_ci#include <sound/core.h> 2262306a36Sopenharmony_ci#include <sound/wss.h> 2362306a36Sopenharmony_ci#include <sound/mpu401.h> 2462306a36Sopenharmony_ci#include <sound/opl4.h> 2562306a36Sopenharmony_ci#include <sound/control.h> 2662306a36Sopenharmony_ci#include <sound/info.h> 2762306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IOPORT 2862306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IRQ 2962306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_DMA 3062306a36Sopenharmony_ci#include <sound/initval.h> 3162306a36Sopenharmony_ci#include <sound/aci.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciMODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>"); 3462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3562306a36Sopenharmony_ciMODULE_DESCRIPTION("Miro miroSOUND PCM1 pro, PCM12, PCM20 Radio"); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ 3862306a36Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ 3962306a36Sopenharmony_cistatic long port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */ 4062306a36Sopenharmony_cistatic long mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */ 4162306a36Sopenharmony_cistatic long fm_port = SNDRV_DEFAULT_PORT1; /* 0x388 */ 4262306a36Sopenharmony_cistatic int irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10,11 */ 4362306a36Sopenharmony_cistatic int mpu_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10 */ 4462306a36Sopenharmony_cistatic int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ 4562306a36Sopenharmony_cistatic int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ 4662306a36Sopenharmony_cistatic int wss; 4762306a36Sopenharmony_cistatic int ide; 4862306a36Sopenharmony_ci#ifdef CONFIG_PNP 4962306a36Sopenharmony_cistatic bool isapnp = 1; /* Enable ISA PnP detection */ 5062306a36Sopenharmony_ci#endif 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cimodule_param(index, int, 0444); 5362306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for miro soundcard."); 5462306a36Sopenharmony_cimodule_param(id, charp, 0444); 5562306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for miro soundcard."); 5662306a36Sopenharmony_cimodule_param_hw(port, long, ioport, 0444); 5762306a36Sopenharmony_ciMODULE_PARM_DESC(port, "WSS port # for miro driver."); 5862306a36Sopenharmony_cimodule_param_hw(mpu_port, long, ioport, 0444); 5962306a36Sopenharmony_ciMODULE_PARM_DESC(mpu_port, "MPU-401 port # for miro driver."); 6062306a36Sopenharmony_cimodule_param_hw(fm_port, long, ioport, 0444); 6162306a36Sopenharmony_ciMODULE_PARM_DESC(fm_port, "FM Port # for miro driver."); 6262306a36Sopenharmony_cimodule_param_hw(irq, int, irq, 0444); 6362306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "WSS irq # for miro driver."); 6462306a36Sopenharmony_cimodule_param_hw(mpu_irq, int, irq, 0444); 6562306a36Sopenharmony_ciMODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for miro driver."); 6662306a36Sopenharmony_cimodule_param_hw(dma1, int, dma, 0444); 6762306a36Sopenharmony_ciMODULE_PARM_DESC(dma1, "1st dma # for miro driver."); 6862306a36Sopenharmony_cimodule_param_hw(dma2, int, dma, 0444); 6962306a36Sopenharmony_ciMODULE_PARM_DESC(dma2, "2nd dma # for miro driver."); 7062306a36Sopenharmony_cimodule_param(wss, int, 0444); 7162306a36Sopenharmony_ciMODULE_PARM_DESC(wss, "wss mode"); 7262306a36Sopenharmony_cimodule_param(ide, int, 0444); 7362306a36Sopenharmony_ciMODULE_PARM_DESC(ide, "enable ide port"); 7462306a36Sopenharmony_ci#ifdef CONFIG_PNP 7562306a36Sopenharmony_cimodule_param(isapnp, bool, 0444); 7662306a36Sopenharmony_ciMODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard."); 7762306a36Sopenharmony_ci#endif 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define OPTi9XX_HW_DETECT 0 8062306a36Sopenharmony_ci#define OPTi9XX_HW_82C928 1 8162306a36Sopenharmony_ci#define OPTi9XX_HW_82C929 2 8262306a36Sopenharmony_ci#define OPTi9XX_HW_82C924 3 8362306a36Sopenharmony_ci#define OPTi9XX_HW_82C925 4 8462306a36Sopenharmony_ci#define OPTi9XX_HW_82C930 5 8562306a36Sopenharmony_ci#define OPTi9XX_HW_82C931 6 8662306a36Sopenharmony_ci#define OPTi9XX_HW_82C933 7 8762306a36Sopenharmony_ci#define OPTi9XX_HW_LAST OPTi9XX_HW_82C933 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define OPTi9XX_MC_REG(n) n 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistruct snd_miro { 9262306a36Sopenharmony_ci unsigned short hardware; 9362306a36Sopenharmony_ci unsigned char password; 9462306a36Sopenharmony_ci char name[7]; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci struct resource *res_mc_base; 9762306a36Sopenharmony_ci struct resource *res_aci_port; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci unsigned long mc_base; 10062306a36Sopenharmony_ci unsigned long mc_base_size; 10162306a36Sopenharmony_ci unsigned long pwd_reg; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci spinlock_t lock; 10462306a36Sopenharmony_ci struct snd_pcm *pcm; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci long wss_base; 10762306a36Sopenharmony_ci int irq; 10862306a36Sopenharmony_ci int dma1; 10962306a36Sopenharmony_ci int dma2; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci long mpu_port; 11262306a36Sopenharmony_ci int mpu_irq; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci struct snd_miro_aci *aci; 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic struct snd_miro_aci aci_device; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic const char * const snd_opti9xx_names[] = { 12062306a36Sopenharmony_ci "unknown", 12162306a36Sopenharmony_ci "82C928", "82C929", 12262306a36Sopenharmony_ci "82C924", "82C925", 12362306a36Sopenharmony_ci "82C930", "82C931", "82C933" 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int snd_miro_pnp_is_probed; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci#ifdef CONFIG_PNP 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic const struct pnp_card_device_id snd_miro_pnpids[] = { 13162306a36Sopenharmony_ci /* PCM20 and PCM12 in PnP mode */ 13262306a36Sopenharmony_ci { .id = "MIR0924", 13362306a36Sopenharmony_ci .devs = { { "MIR0000" }, { "MIR0002" }, { "MIR0005" } }, }, 13462306a36Sopenharmony_ci { .id = "" } 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_miro_pnpids); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#endif /* CONFIG_PNP */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* 14262306a36Sopenharmony_ci * ACI control 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int aci_busy_wait(struct snd_miro_aci *aci) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci long timeout; 14862306a36Sopenharmony_ci unsigned char byte; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci for (timeout = 1; timeout <= ACI_MINTIME + 30; timeout++) { 15162306a36Sopenharmony_ci byte = inb(aci->aci_port + ACI_REG_BUSY); 15262306a36Sopenharmony_ci if ((byte & 1) == 0) { 15362306a36Sopenharmony_ci if (timeout >= ACI_MINTIME) 15462306a36Sopenharmony_ci snd_printd("aci ready in round %ld.\n", 15562306a36Sopenharmony_ci timeout-ACI_MINTIME); 15662306a36Sopenharmony_ci return byte; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci if (timeout >= ACI_MINTIME) { 15962306a36Sopenharmony_ci long out=10*HZ; 16062306a36Sopenharmony_ci switch (timeout-ACI_MINTIME) { 16162306a36Sopenharmony_ci case 0 ... 9: 16262306a36Sopenharmony_ci out /= 10; 16362306a36Sopenharmony_ci fallthrough; 16462306a36Sopenharmony_ci case 10 ... 19: 16562306a36Sopenharmony_ci out /= 10; 16662306a36Sopenharmony_ci fallthrough; 16762306a36Sopenharmony_ci case 20 ... 30: 16862306a36Sopenharmony_ci out /= 10; 16962306a36Sopenharmony_ci fallthrough; 17062306a36Sopenharmony_ci default: 17162306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 17262306a36Sopenharmony_ci schedule_timeout(out); 17362306a36Sopenharmony_ci break; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci snd_printk(KERN_ERR "aci_busy_wait() time out\n"); 17862306a36Sopenharmony_ci return -EBUSY; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic inline int aci_write(struct snd_miro_aci *aci, unsigned char byte) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci if (aci_busy_wait(aci) >= 0) { 18462306a36Sopenharmony_ci outb(byte, aci->aci_port + ACI_REG_COMMAND); 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci } else { 18762306a36Sopenharmony_ci snd_printk(KERN_ERR "aci busy, aci_write(0x%x) stopped.\n", byte); 18862306a36Sopenharmony_ci return -EBUSY; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic inline int aci_read(struct snd_miro_aci *aci) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci unsigned char byte; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (aci_busy_wait(aci) >= 0) { 19762306a36Sopenharmony_ci byte = inb(aci->aci_port + ACI_REG_STATUS); 19862306a36Sopenharmony_ci return byte; 19962306a36Sopenharmony_ci } else { 20062306a36Sopenharmony_ci snd_printk(KERN_ERR "aci busy, aci_read() stopped.\n"); 20162306a36Sopenharmony_ci return -EBUSY; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ciint snd_aci_cmd(struct snd_miro_aci *aci, int write1, int write2, int write3) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci int write[] = {write1, write2, write3}; 20862306a36Sopenharmony_ci int value, i; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (mutex_lock_interruptible(&aci->aci_mutex)) 21162306a36Sopenharmony_ci return -EINTR; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci for (i=0; i<3; i++) { 21462306a36Sopenharmony_ci if (write[i]< 0 || write[i] > 255) 21562306a36Sopenharmony_ci break; 21662306a36Sopenharmony_ci else { 21762306a36Sopenharmony_ci value = aci_write(aci, write[i]); 21862306a36Sopenharmony_ci if (value < 0) 21962306a36Sopenharmony_ci goto out; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci value = aci_read(aci); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ciout: mutex_unlock(&aci->aci_mutex); 22662306a36Sopenharmony_ci return value; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_aci_cmd); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int aci_getvalue(struct snd_miro_aci *aci, unsigned char index) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci return snd_aci_cmd(aci, ACI_STATUS, index, -1); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int aci_setvalue(struct snd_miro_aci *aci, unsigned char index, 23662306a36Sopenharmony_ci int value) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci return snd_aci_cmd(aci, index, value, -1); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistruct snd_miro_aci *snd_aci_get_aci(void) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci if (aci_device.aci_port == 0) 24462306a36Sopenharmony_ci return NULL; 24562306a36Sopenharmony_ci return &aci_device; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_aci_get_aci); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/* 25062306a36Sopenharmony_ci * MIXER part 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci#define snd_miro_info_capture snd_ctl_boolean_mono_info 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic int snd_miro_get_capture(struct snd_kcontrol *kcontrol, 25662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 25962306a36Sopenharmony_ci int value; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci value = aci_getvalue(miro->aci, ACI_S_GENERAL); 26262306a36Sopenharmony_ci if (value < 0) { 26362306a36Sopenharmony_ci snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n", 26462306a36Sopenharmony_ci value); 26562306a36Sopenharmony_ci return value; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci ucontrol->value.integer.value[0] = value & 0x20; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int snd_miro_put_capture(struct snd_kcontrol *kcontrol, 27462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 27762306a36Sopenharmony_ci int change, value, error; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci value = !(ucontrol->value.integer.value[0]); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci error = aci_setvalue(miro->aci, ACI_SET_SOLOMODE, value); 28262306a36Sopenharmony_ci if (error < 0) { 28362306a36Sopenharmony_ci snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n", 28462306a36Sopenharmony_ci error); 28562306a36Sopenharmony_ci return error; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci change = (value != miro->aci->aci_solomode); 28962306a36Sopenharmony_ci miro->aci->aci_solomode = value; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return change; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int snd_miro_info_preamp(struct snd_kcontrol *kcontrol, 29562306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 29862306a36Sopenharmony_ci uinfo->count = 1; 29962306a36Sopenharmony_ci uinfo->value.integer.min = 0; 30062306a36Sopenharmony_ci uinfo->value.integer.max = 3; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int snd_miro_get_preamp(struct snd_kcontrol *kcontrol, 30662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 30962306a36Sopenharmony_ci int value; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (miro->aci->aci_version <= 176) { 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* 31462306a36Sopenharmony_ci OSS says it's not readable with versions < 176. 31562306a36Sopenharmony_ci But it doesn't work on my card, 31662306a36Sopenharmony_ci which is a PCM12 with aci_version = 176. 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci ucontrol->value.integer.value[0] = miro->aci->aci_preamp; 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci value = aci_getvalue(miro->aci, ACI_GET_PREAMP); 32462306a36Sopenharmony_ci if (value < 0) { 32562306a36Sopenharmony_ci snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n", 32662306a36Sopenharmony_ci value); 32762306a36Sopenharmony_ci return value; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci ucontrol->value.integer.value[0] = value; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int snd_miro_put_preamp(struct snd_kcontrol *kcontrol, 33662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 33962306a36Sopenharmony_ci int error, value, change; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci value = ucontrol->value.integer.value[0]; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci error = aci_setvalue(miro->aci, ACI_SET_PREAMP, value); 34462306a36Sopenharmony_ci if (error < 0) { 34562306a36Sopenharmony_ci snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n", 34662306a36Sopenharmony_ci error); 34762306a36Sopenharmony_ci return error; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci change = (value != miro->aci->aci_preamp); 35162306a36Sopenharmony_ci miro->aci->aci_preamp = value; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return change; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci#define snd_miro_info_amp snd_ctl_boolean_mono_info 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic int snd_miro_get_amp(struct snd_kcontrol *kcontrol, 35962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 36262306a36Sopenharmony_ci ucontrol->value.integer.value[0] = miro->aci->aci_amp; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int snd_miro_put_amp(struct snd_kcontrol *kcontrol, 36862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 37162306a36Sopenharmony_ci int error, value, change; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci value = ucontrol->value.integer.value[0]; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci error = aci_setvalue(miro->aci, ACI_SET_POWERAMP, value); 37662306a36Sopenharmony_ci if (error < 0) { 37762306a36Sopenharmony_ci snd_printk(KERN_ERR "snd_miro_put_amp() to %d failed: %d\n", value, error); 37862306a36Sopenharmony_ci return error; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci change = (value != miro->aci->aci_amp); 38262306a36Sopenharmony_ci miro->aci->aci_amp = value; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci return change; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci#define MIRO_DOUBLE(ctl_name, ctl_index, get_right_reg, set_right_reg) \ 38862306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 38962306a36Sopenharmony_ci .name = ctl_name, \ 39062306a36Sopenharmony_ci .index = ctl_index, \ 39162306a36Sopenharmony_ci .info = snd_miro_info_double, \ 39262306a36Sopenharmony_ci .get = snd_miro_get_double, \ 39362306a36Sopenharmony_ci .put = snd_miro_put_double, \ 39462306a36Sopenharmony_ci .private_value = get_right_reg | (set_right_reg << 8) \ 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic int snd_miro_info_double(struct snd_kcontrol *kcontrol, 39862306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 40362306a36Sopenharmony_ci uinfo->count = 2; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if ((reg >= ACI_GET_EQ1) && (reg <= ACI_GET_EQ7)) { 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* equalizer elements */ 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci uinfo->value.integer.min = - 0x7f; 41062306a36Sopenharmony_ci uinfo->value.integer.max = 0x7f; 41162306a36Sopenharmony_ci } else { 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* non-equalizer elements */ 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci uinfo->value.integer.min = 0; 41662306a36Sopenharmony_ci uinfo->value.integer.max = 0x20; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int snd_miro_get_double(struct snd_kcontrol *kcontrol, 42362306a36Sopenharmony_ci struct snd_ctl_elem_value *uinfo) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 42662306a36Sopenharmony_ci int left_val, right_val; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci int right_reg = kcontrol->private_value & 0xff; 42962306a36Sopenharmony_ci int left_reg = right_reg + 1; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci right_val = aci_getvalue(miro->aci, right_reg); 43262306a36Sopenharmony_ci if (right_val < 0) { 43362306a36Sopenharmony_ci snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", right_reg, right_val); 43462306a36Sopenharmony_ci return right_val; 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci left_val = aci_getvalue(miro->aci, left_reg); 43862306a36Sopenharmony_ci if (left_val < 0) { 43962306a36Sopenharmony_ci snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", left_reg, left_val); 44062306a36Sopenharmony_ci return left_val; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if ((right_reg >= ACI_GET_EQ1) && (right_reg <= ACI_GET_EQ7)) { 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* equalizer elements */ 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (left_val < 0x80) { 44862306a36Sopenharmony_ci uinfo->value.integer.value[0] = left_val; 44962306a36Sopenharmony_ci } else { 45062306a36Sopenharmony_ci uinfo->value.integer.value[0] = 0x80 - left_val; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (right_val < 0x80) { 45462306a36Sopenharmony_ci uinfo->value.integer.value[1] = right_val; 45562306a36Sopenharmony_ci } else { 45662306a36Sopenharmony_ci uinfo->value.integer.value[1] = 0x80 - right_val; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci } else { 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* non-equalizer elements */ 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci uinfo->value.integer.value[0] = 0x20 - left_val; 46462306a36Sopenharmony_ci uinfo->value.integer.value[1] = 0x20 - right_val; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci return 0; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic int snd_miro_put_double(struct snd_kcontrol *kcontrol, 47162306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 47462306a36Sopenharmony_ci struct snd_miro_aci *aci = miro->aci; 47562306a36Sopenharmony_ci int left, right, left_old, right_old; 47662306a36Sopenharmony_ci int setreg_left, setreg_right, getreg_left, getreg_right; 47762306a36Sopenharmony_ci int change, error; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci left = ucontrol->value.integer.value[0]; 48062306a36Sopenharmony_ci right = ucontrol->value.integer.value[1]; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci setreg_right = (kcontrol->private_value >> 8) & 0xff; 48362306a36Sopenharmony_ci setreg_left = setreg_right + 8; 48462306a36Sopenharmony_ci if (setreg_right == ACI_SET_MASTER) 48562306a36Sopenharmony_ci setreg_left -= 7; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci getreg_right = kcontrol->private_value & 0xff; 48862306a36Sopenharmony_ci getreg_left = getreg_right + 1; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci left_old = aci_getvalue(aci, getreg_left); 49162306a36Sopenharmony_ci if (left_old < 0) { 49262306a36Sopenharmony_ci snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_left, left_old); 49362306a36Sopenharmony_ci return left_old; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci right_old = aci_getvalue(aci, getreg_right); 49762306a36Sopenharmony_ci if (right_old < 0) { 49862306a36Sopenharmony_ci snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_right, right_old); 49962306a36Sopenharmony_ci return right_old; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if ((getreg_right >= ACI_GET_EQ1) && (getreg_right <= ACI_GET_EQ7)) { 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* equalizer elements */ 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (left < -0x7f || left > 0x7f || 50762306a36Sopenharmony_ci right < -0x7f || right > 0x7f) 50862306a36Sopenharmony_ci return -EINVAL; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (left_old > 0x80) 51162306a36Sopenharmony_ci left_old = 0x80 - left_old; 51262306a36Sopenharmony_ci if (right_old > 0x80) 51362306a36Sopenharmony_ci right_old = 0x80 - right_old; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (left >= 0) { 51662306a36Sopenharmony_ci error = aci_setvalue(aci, setreg_left, left); 51762306a36Sopenharmony_ci if (error < 0) { 51862306a36Sopenharmony_ci snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 51962306a36Sopenharmony_ci left, error); 52062306a36Sopenharmony_ci return error; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci } else { 52362306a36Sopenharmony_ci error = aci_setvalue(aci, setreg_left, 0x80 - left); 52462306a36Sopenharmony_ci if (error < 0) { 52562306a36Sopenharmony_ci snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 52662306a36Sopenharmony_ci 0x80 - left, error); 52762306a36Sopenharmony_ci return error; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (right >= 0) { 53262306a36Sopenharmony_ci error = aci_setvalue(aci, setreg_right, right); 53362306a36Sopenharmony_ci if (error < 0) { 53462306a36Sopenharmony_ci snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 53562306a36Sopenharmony_ci right, error); 53662306a36Sopenharmony_ci return error; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci } else { 53962306a36Sopenharmony_ci error = aci_setvalue(aci, setreg_right, 0x80 - right); 54062306a36Sopenharmony_ci if (error < 0) { 54162306a36Sopenharmony_ci snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 54262306a36Sopenharmony_ci 0x80 - right, error); 54362306a36Sopenharmony_ci return error; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci } else { 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* non-equalizer elements */ 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (left < 0 || left > 0x20 || 55262306a36Sopenharmony_ci right < 0 || right > 0x20) 55362306a36Sopenharmony_ci return -EINVAL; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci left_old = 0x20 - left_old; 55662306a36Sopenharmony_ci right_old = 0x20 - right_old; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci error = aci_setvalue(aci, setreg_left, 0x20 - left); 55962306a36Sopenharmony_ci if (error < 0) { 56062306a36Sopenharmony_ci snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 56162306a36Sopenharmony_ci 0x20 - left, error); 56262306a36Sopenharmony_ci return error; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci error = aci_setvalue(aci, setreg_right, 0x20 - right); 56562306a36Sopenharmony_ci if (error < 0) { 56662306a36Sopenharmony_ci snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 56762306a36Sopenharmony_ci 0x20 - right, error); 56862306a36Sopenharmony_ci return error; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci change = (left != left_old) || (right != right_old); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return change; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_controls[] = { 57862306a36Sopenharmony_ciMIRO_DOUBLE("Master Playback Volume", 0, ACI_GET_MASTER, ACI_SET_MASTER), 57962306a36Sopenharmony_ciMIRO_DOUBLE("Mic Playback Volume", 1, ACI_GET_MIC, ACI_SET_MIC), 58062306a36Sopenharmony_ciMIRO_DOUBLE("Line Playback Volume", 1, ACI_GET_LINE, ACI_SET_LINE), 58162306a36Sopenharmony_ciMIRO_DOUBLE("CD Playback Volume", 0, ACI_GET_CD, ACI_SET_CD), 58262306a36Sopenharmony_ciMIRO_DOUBLE("Synth Playback Volume", 0, ACI_GET_SYNTH, ACI_SET_SYNTH), 58362306a36Sopenharmony_ciMIRO_DOUBLE("PCM Playback Volume", 1, ACI_GET_PCM, ACI_SET_PCM), 58462306a36Sopenharmony_ciMIRO_DOUBLE("Aux Playback Volume", 2, ACI_GET_LINE2, ACI_SET_LINE2), 58562306a36Sopenharmony_ci}; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci/* Equalizer with seven bands (only PCM20) 58862306a36Sopenharmony_ci from -12dB up to +12dB on each band */ 58962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_eq_controls[] = { 59062306a36Sopenharmony_ciMIRO_DOUBLE("Tone Control - 28 Hz", 0, ACI_GET_EQ1, ACI_SET_EQ1), 59162306a36Sopenharmony_ciMIRO_DOUBLE("Tone Control - 160 Hz", 0, ACI_GET_EQ2, ACI_SET_EQ2), 59262306a36Sopenharmony_ciMIRO_DOUBLE("Tone Control - 400 Hz", 0, ACI_GET_EQ3, ACI_SET_EQ3), 59362306a36Sopenharmony_ciMIRO_DOUBLE("Tone Control - 1 kHz", 0, ACI_GET_EQ4, ACI_SET_EQ4), 59462306a36Sopenharmony_ciMIRO_DOUBLE("Tone Control - 2.5 kHz", 0, ACI_GET_EQ5, ACI_SET_EQ5), 59562306a36Sopenharmony_ciMIRO_DOUBLE("Tone Control - 6.3 kHz", 0, ACI_GET_EQ6, ACI_SET_EQ6), 59662306a36Sopenharmony_ciMIRO_DOUBLE("Tone Control - 16 kHz", 0, ACI_GET_EQ7, ACI_SET_EQ7), 59762306a36Sopenharmony_ci}; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_radio_control[] = { 60062306a36Sopenharmony_ciMIRO_DOUBLE("Radio Playback Volume", 0, ACI_GET_LINE1, ACI_SET_LINE1), 60162306a36Sopenharmony_ci}; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_line_control[] = { 60462306a36Sopenharmony_ciMIRO_DOUBLE("Line Playback Volume", 2, ACI_GET_LINE1, ACI_SET_LINE1), 60562306a36Sopenharmony_ci}; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_preamp_control[] = { 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 61062306a36Sopenharmony_ci .name = "Mic Boost", 61162306a36Sopenharmony_ci .index = 1, 61262306a36Sopenharmony_ci .info = snd_miro_info_preamp, 61362306a36Sopenharmony_ci .get = snd_miro_get_preamp, 61462306a36Sopenharmony_ci .put = snd_miro_put_preamp, 61562306a36Sopenharmony_ci}}; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_amp_control[] = { 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 62062306a36Sopenharmony_ci .name = "Line Boost", 62162306a36Sopenharmony_ci .index = 0, 62262306a36Sopenharmony_ci .info = snd_miro_info_amp, 62362306a36Sopenharmony_ci .get = snd_miro_get_amp, 62462306a36Sopenharmony_ci .put = snd_miro_put_amp, 62562306a36Sopenharmony_ci}}; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_capture_control[] = { 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 63062306a36Sopenharmony_ci .name = "PCM Capture Switch", 63162306a36Sopenharmony_ci .index = 0, 63262306a36Sopenharmony_ci .info = snd_miro_info_capture, 63362306a36Sopenharmony_ci .get = snd_miro_get_capture, 63462306a36Sopenharmony_ci .put = snd_miro_put_capture, 63562306a36Sopenharmony_ci}}; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic const unsigned char aci_init_values[][2] = { 63862306a36Sopenharmony_ci { ACI_SET_MUTE, 0x00 }, 63962306a36Sopenharmony_ci { ACI_SET_POWERAMP, 0x00 }, 64062306a36Sopenharmony_ci { ACI_SET_PREAMP, 0x00 }, 64162306a36Sopenharmony_ci { ACI_SET_SOLOMODE, 0x00 }, 64262306a36Sopenharmony_ci { ACI_SET_MIC + 0, 0x20 }, 64362306a36Sopenharmony_ci { ACI_SET_MIC + 8, 0x20 }, 64462306a36Sopenharmony_ci { ACI_SET_LINE + 0, 0x20 }, 64562306a36Sopenharmony_ci { ACI_SET_LINE + 8, 0x20 }, 64662306a36Sopenharmony_ci { ACI_SET_CD + 0, 0x20 }, 64762306a36Sopenharmony_ci { ACI_SET_CD + 8, 0x20 }, 64862306a36Sopenharmony_ci { ACI_SET_PCM + 0, 0x20 }, 64962306a36Sopenharmony_ci { ACI_SET_PCM + 8, 0x20 }, 65062306a36Sopenharmony_ci { ACI_SET_LINE1 + 0, 0x20 }, 65162306a36Sopenharmony_ci { ACI_SET_LINE1 + 8, 0x20 }, 65262306a36Sopenharmony_ci { ACI_SET_LINE2 + 0, 0x20 }, 65362306a36Sopenharmony_ci { ACI_SET_LINE2 + 8, 0x20 }, 65462306a36Sopenharmony_ci { ACI_SET_SYNTH + 0, 0x20 }, 65562306a36Sopenharmony_ci { ACI_SET_SYNTH + 8, 0x20 }, 65662306a36Sopenharmony_ci { ACI_SET_MASTER + 0, 0x20 }, 65762306a36Sopenharmony_ci { ACI_SET_MASTER + 1, 0x20 }, 65862306a36Sopenharmony_ci}; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic int snd_set_aci_init_values(struct snd_miro *miro) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci int idx, error; 66362306a36Sopenharmony_ci struct snd_miro_aci *aci = miro->aci; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* enable WSS on PCM1 */ 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if ((aci->aci_product == 'A') && wss) { 66862306a36Sopenharmony_ci error = aci_setvalue(aci, ACI_SET_WSS, wss); 66962306a36Sopenharmony_ci if (error < 0) { 67062306a36Sopenharmony_ci snd_printk(KERN_ERR "enabling WSS mode failed\n"); 67162306a36Sopenharmony_ci return error; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* enable IDE port */ 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (ide) { 67862306a36Sopenharmony_ci error = aci_setvalue(aci, ACI_SET_IDE, ide); 67962306a36Sopenharmony_ci if (error < 0) { 68062306a36Sopenharmony_ci snd_printk(KERN_ERR "enabling IDE port failed\n"); 68162306a36Sopenharmony_ci return error; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* set common aci values */ 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(aci_init_values); idx++) { 68862306a36Sopenharmony_ci error = aci_setvalue(aci, aci_init_values[idx][0], 68962306a36Sopenharmony_ci aci_init_values[idx][1]); 69062306a36Sopenharmony_ci if (error < 0) { 69162306a36Sopenharmony_ci snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 69262306a36Sopenharmony_ci aci_init_values[idx][0], error); 69362306a36Sopenharmony_ci return error; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci aci->aci_amp = 0; 69762306a36Sopenharmony_ci aci->aci_preamp = 0; 69862306a36Sopenharmony_ci aci->aci_solomode = 1; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci return 0; 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic int snd_miro_mixer(struct snd_card *card, 70462306a36Sopenharmony_ci struct snd_miro *miro) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci unsigned int idx; 70762306a36Sopenharmony_ci int err; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (snd_BUG_ON(!miro || !card)) 71062306a36Sopenharmony_ci return -EINVAL; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci switch (miro->hardware) { 71362306a36Sopenharmony_ci case OPTi9XX_HW_82C924: 71462306a36Sopenharmony_ci strcpy(card->mixername, "ACI & OPTi924"); 71562306a36Sopenharmony_ci break; 71662306a36Sopenharmony_ci case OPTi9XX_HW_82C929: 71762306a36Sopenharmony_ci strcpy(card->mixername, "ACI & OPTi929"); 71862306a36Sopenharmony_ci break; 71962306a36Sopenharmony_ci default: 72062306a36Sopenharmony_ci snd_BUG(); 72162306a36Sopenharmony_ci break; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_miro_controls); idx++) { 72562306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_controls[idx], miro)); 72662306a36Sopenharmony_ci if (err < 0) 72762306a36Sopenharmony_ci return err; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if ((miro->aci->aci_product == 'A') || 73162306a36Sopenharmony_ci (miro->aci->aci_product == 'B')) { 73262306a36Sopenharmony_ci /* PCM1/PCM12 with power-amp and Line 2 */ 73362306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_line_control[0], miro)); 73462306a36Sopenharmony_ci if (err < 0) 73562306a36Sopenharmony_ci return err; 73662306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_amp_control[0], miro)); 73762306a36Sopenharmony_ci if (err < 0) 73862306a36Sopenharmony_ci return err; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if ((miro->aci->aci_product == 'B') || 74262306a36Sopenharmony_ci (miro->aci->aci_product == 'C')) { 74362306a36Sopenharmony_ci /* PCM12/PCM20 with mic-preamp */ 74462306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_preamp_control[0], miro)); 74562306a36Sopenharmony_ci if (err < 0) 74662306a36Sopenharmony_ci return err; 74762306a36Sopenharmony_ci if (miro->aci->aci_version >= 176) { 74862306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro)); 74962306a36Sopenharmony_ci if (err < 0) 75062306a36Sopenharmony_ci return err; 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (miro->aci->aci_product == 'C') { 75562306a36Sopenharmony_ci /* PCM20 with radio and 7 band equalizer */ 75662306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_radio_control[0], miro)); 75762306a36Sopenharmony_ci if (err < 0) 75862306a36Sopenharmony_ci return err; 75962306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_miro_eq_controls); idx++) { 76062306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_eq_controls[idx], miro)); 76162306a36Sopenharmony_ci if (err < 0) 76262306a36Sopenharmony_ci return err; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci return 0; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic int snd_miro_init(struct snd_miro *chip, 77062306a36Sopenharmony_ci unsigned short hardware) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci chip->hardware = hardware; 77562306a36Sopenharmony_ci strcpy(chip->name, snd_opti9xx_names[hardware]); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci chip->mc_base_size = opti9xx_mc_size[hardware]; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci spin_lock_init(&chip->lock); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci chip->wss_base = -1; 78262306a36Sopenharmony_ci chip->irq = -1; 78362306a36Sopenharmony_ci chip->dma1 = -1; 78462306a36Sopenharmony_ci chip->dma2 = -1; 78562306a36Sopenharmony_ci chip->mpu_port = -1; 78662306a36Sopenharmony_ci chip->mpu_irq = -1; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci chip->pwd_reg = 3; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci#ifdef CONFIG_PNP 79162306a36Sopenharmony_ci if (isapnp && chip->mc_base) 79262306a36Sopenharmony_ci /* PnP resource gives the least 10 bits */ 79362306a36Sopenharmony_ci chip->mc_base |= 0xc00; 79462306a36Sopenharmony_ci else 79562306a36Sopenharmony_ci#endif 79662306a36Sopenharmony_ci chip->mc_base = 0xf8c; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci switch (hardware) { 79962306a36Sopenharmony_ci case OPTi9XX_HW_82C929: 80062306a36Sopenharmony_ci chip->password = 0xe3; 80162306a36Sopenharmony_ci break; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci case OPTi9XX_HW_82C924: 80462306a36Sopenharmony_ci chip->password = 0xe5; 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci default: 80862306a36Sopenharmony_ci snd_printk(KERN_ERR "sorry, no support for %d\n", hardware); 80962306a36Sopenharmony_ci return -ENODEV; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci return 0; 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic unsigned char snd_miro_read(struct snd_miro *chip, 81662306a36Sopenharmony_ci unsigned char reg) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci unsigned long flags; 81962306a36Sopenharmony_ci unsigned char retval = 0xff; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 82262306a36Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci switch (chip->hardware) { 82562306a36Sopenharmony_ci case OPTi9XX_HW_82C924: 82662306a36Sopenharmony_ci if (reg > 7) { 82762306a36Sopenharmony_ci outb(reg, chip->mc_base + 8); 82862306a36Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 82962306a36Sopenharmony_ci retval = inb(chip->mc_base + 9); 83062306a36Sopenharmony_ci break; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci fallthrough; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci case OPTi9XX_HW_82C929: 83562306a36Sopenharmony_ci retval = inb(chip->mc_base + reg); 83662306a36Sopenharmony_ci break; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci default: 83962306a36Sopenharmony_ci snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware); 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 84362306a36Sopenharmony_ci return retval; 84462306a36Sopenharmony_ci} 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_cistatic void snd_miro_write(struct snd_miro *chip, unsigned char reg, 84762306a36Sopenharmony_ci unsigned char value) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci unsigned long flags; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 85262306a36Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci switch (chip->hardware) { 85562306a36Sopenharmony_ci case OPTi9XX_HW_82C924: 85662306a36Sopenharmony_ci if (reg > 7) { 85762306a36Sopenharmony_ci outb(reg, chip->mc_base + 8); 85862306a36Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 85962306a36Sopenharmony_ci outb(value, chip->mc_base + 9); 86062306a36Sopenharmony_ci break; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci fallthrough; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci case OPTi9XX_HW_82C929: 86562306a36Sopenharmony_ci outb(value, chip->mc_base + reg); 86662306a36Sopenharmony_ci break; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci default: 86962306a36Sopenharmony_ci snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware); 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic inline void snd_miro_write_mask(struct snd_miro *chip, 87662306a36Sopenharmony_ci unsigned char reg, unsigned char value, unsigned char mask) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci unsigned char oldval = snd_miro_read(chip, reg); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci snd_miro_write(chip, reg, (oldval & ~mask) | (value & mask)); 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci/* 88462306a36Sopenharmony_ci * Proc Interface 88562306a36Sopenharmony_ci */ 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic void snd_miro_proc_read(struct snd_info_entry * entry, 88862306a36Sopenharmony_ci struct snd_info_buffer *buffer) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct snd_miro *miro = (struct snd_miro *) entry->private_data; 89162306a36Sopenharmony_ci struct snd_miro_aci *aci = miro->aci; 89262306a36Sopenharmony_ci char* model = "unknown"; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* miroSOUND PCM1 pro, early PCM12 */ 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci if ((miro->hardware == OPTi9XX_HW_82C929) && 89762306a36Sopenharmony_ci (aci->aci_vendor == 'm') && 89862306a36Sopenharmony_ci (aci->aci_product == 'A')) { 89962306a36Sopenharmony_ci switch (aci->aci_version) { 90062306a36Sopenharmony_ci case 3: 90162306a36Sopenharmony_ci model = "miroSOUND PCM1 pro"; 90262306a36Sopenharmony_ci break; 90362306a36Sopenharmony_ci default: 90462306a36Sopenharmony_ci model = "miroSOUND PCM1 pro / (early) PCM12"; 90562306a36Sopenharmony_ci break; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci /* miroSOUND PCM12, PCM12 (Rev. E), PCM12 pnp */ 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if ((miro->hardware == OPTi9XX_HW_82C924) && 91262306a36Sopenharmony_ci (aci->aci_vendor == 'm') && 91362306a36Sopenharmony_ci (aci->aci_product == 'B')) { 91462306a36Sopenharmony_ci switch (aci->aci_version) { 91562306a36Sopenharmony_ci case 4: 91662306a36Sopenharmony_ci model = "miroSOUND PCM12"; 91762306a36Sopenharmony_ci break; 91862306a36Sopenharmony_ci case 176: 91962306a36Sopenharmony_ci model = "miroSOUND PCM12 (Rev. E)"; 92062306a36Sopenharmony_ci break; 92162306a36Sopenharmony_ci default: 92262306a36Sopenharmony_ci model = "miroSOUND PCM12 / PCM12 pnp"; 92362306a36Sopenharmony_ci break; 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci /* miroSOUND PCM20 radio */ 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if ((miro->hardware == OPTi9XX_HW_82C924) && 93062306a36Sopenharmony_ci (aci->aci_vendor == 'm') && 93162306a36Sopenharmony_ci (aci->aci_product == 'C')) { 93262306a36Sopenharmony_ci switch (aci->aci_version) { 93362306a36Sopenharmony_ci case 7: 93462306a36Sopenharmony_ci model = "miroSOUND PCM20 radio (Rev. E)"; 93562306a36Sopenharmony_ci break; 93662306a36Sopenharmony_ci default: 93762306a36Sopenharmony_ci model = "miroSOUND PCM20 radio"; 93862306a36Sopenharmony_ci break; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci snd_iprintf(buffer, "\nGeneral information:\n"); 94362306a36Sopenharmony_ci snd_iprintf(buffer, " model : %s\n", model); 94462306a36Sopenharmony_ci snd_iprintf(buffer, " opti : %s\n", miro->name); 94562306a36Sopenharmony_ci snd_iprintf(buffer, " codec : %s\n", miro->pcm->name); 94662306a36Sopenharmony_ci snd_iprintf(buffer, " port : 0x%lx\n", miro->wss_base); 94762306a36Sopenharmony_ci snd_iprintf(buffer, " irq : %d\n", miro->irq); 94862306a36Sopenharmony_ci snd_iprintf(buffer, " dma : %d,%d\n\n", miro->dma1, miro->dma2); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci snd_iprintf(buffer, "MPU-401:\n"); 95162306a36Sopenharmony_ci snd_iprintf(buffer, " port : 0x%lx\n", miro->mpu_port); 95262306a36Sopenharmony_ci snd_iprintf(buffer, " irq : %d\n\n", miro->mpu_irq); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci snd_iprintf(buffer, "ACI information:\n"); 95562306a36Sopenharmony_ci snd_iprintf(buffer, " vendor : "); 95662306a36Sopenharmony_ci switch (aci->aci_vendor) { 95762306a36Sopenharmony_ci case 'm': 95862306a36Sopenharmony_ci snd_iprintf(buffer, "Miro\n"); 95962306a36Sopenharmony_ci break; 96062306a36Sopenharmony_ci default: 96162306a36Sopenharmony_ci snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_vendor); 96262306a36Sopenharmony_ci break; 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci snd_iprintf(buffer, " product : "); 96662306a36Sopenharmony_ci switch (aci->aci_product) { 96762306a36Sopenharmony_ci case 'A': 96862306a36Sopenharmony_ci snd_iprintf(buffer, "miroSOUND PCM1 pro / (early) PCM12\n"); 96962306a36Sopenharmony_ci break; 97062306a36Sopenharmony_ci case 'B': 97162306a36Sopenharmony_ci snd_iprintf(buffer, "miroSOUND PCM12\n"); 97262306a36Sopenharmony_ci break; 97362306a36Sopenharmony_ci case 'C': 97462306a36Sopenharmony_ci snd_iprintf(buffer, "miroSOUND PCM20 radio\n"); 97562306a36Sopenharmony_ci break; 97662306a36Sopenharmony_ci default: 97762306a36Sopenharmony_ci snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_product); 97862306a36Sopenharmony_ci break; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci snd_iprintf(buffer, " firmware: %d (0x%x)\n", 98262306a36Sopenharmony_ci aci->aci_version, aci->aci_version); 98362306a36Sopenharmony_ci snd_iprintf(buffer, " port : 0x%lx-0x%lx\n", 98462306a36Sopenharmony_ci aci->aci_port, aci->aci_port+2); 98562306a36Sopenharmony_ci snd_iprintf(buffer, " wss : 0x%x\n", wss); 98662306a36Sopenharmony_ci snd_iprintf(buffer, " ide : 0x%x\n", ide); 98762306a36Sopenharmony_ci snd_iprintf(buffer, " solomode: 0x%x\n", aci->aci_solomode); 98862306a36Sopenharmony_ci snd_iprintf(buffer, " amp : 0x%x\n", aci->aci_amp); 98962306a36Sopenharmony_ci snd_iprintf(buffer, " preamp : 0x%x\n", aci->aci_preamp); 99062306a36Sopenharmony_ci} 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cistatic void snd_miro_proc_init(struct snd_card *card, 99362306a36Sopenharmony_ci struct snd_miro *miro) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci snd_card_ro_proc_new(card, "miro", miro, snd_miro_proc_read); 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci/* 99962306a36Sopenharmony_ci * Init 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cistatic int snd_miro_configure(struct snd_miro *chip) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci unsigned char wss_base_bits; 100562306a36Sopenharmony_ci unsigned char irq_bits; 100662306a36Sopenharmony_ci unsigned char dma_bits; 100762306a36Sopenharmony_ci unsigned char mpu_port_bits = 0; 100862306a36Sopenharmony_ci unsigned char mpu_irq_bits; 100962306a36Sopenharmony_ci unsigned long flags; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); 101262306a36Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */ 101362306a36Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci switch (chip->hardware) { 101662306a36Sopenharmony_ci case OPTi9XX_HW_82C924: 101762306a36Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02); 101862306a36Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff); 101962306a36Sopenharmony_ci break; 102062306a36Sopenharmony_ci case OPTi9XX_HW_82C929: 102162306a36Sopenharmony_ci /* untested init commands for OPTi929 */ 102262306a36Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c); 102362306a36Sopenharmony_ci break; 102462306a36Sopenharmony_ci default: 102562306a36Sopenharmony_ci snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 102662306a36Sopenharmony_ci return -EINVAL; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci /* PnP resource says it decodes only 10 bits of address */ 103062306a36Sopenharmony_ci switch (chip->wss_base & 0x3ff) { 103162306a36Sopenharmony_ci case 0x130: 103262306a36Sopenharmony_ci chip->wss_base = 0x530; 103362306a36Sopenharmony_ci wss_base_bits = 0x00; 103462306a36Sopenharmony_ci break; 103562306a36Sopenharmony_ci case 0x204: 103662306a36Sopenharmony_ci chip->wss_base = 0x604; 103762306a36Sopenharmony_ci wss_base_bits = 0x03; 103862306a36Sopenharmony_ci break; 103962306a36Sopenharmony_ci case 0x280: 104062306a36Sopenharmony_ci chip->wss_base = 0xe80; 104162306a36Sopenharmony_ci wss_base_bits = 0x01; 104262306a36Sopenharmony_ci break; 104362306a36Sopenharmony_ci case 0x340: 104462306a36Sopenharmony_ci chip->wss_base = 0xf40; 104562306a36Sopenharmony_ci wss_base_bits = 0x02; 104662306a36Sopenharmony_ci break; 104762306a36Sopenharmony_ci default: 104862306a36Sopenharmony_ci snd_printk(KERN_ERR "WSS port 0x%lx not valid\n", chip->wss_base); 104962306a36Sopenharmony_ci goto __skip_base; 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci__skip_base: 105462306a36Sopenharmony_ci switch (chip->irq) { 105562306a36Sopenharmony_ci case 5: 105662306a36Sopenharmony_ci irq_bits = 0x05; 105762306a36Sopenharmony_ci break; 105862306a36Sopenharmony_ci case 7: 105962306a36Sopenharmony_ci irq_bits = 0x01; 106062306a36Sopenharmony_ci break; 106162306a36Sopenharmony_ci case 9: 106262306a36Sopenharmony_ci irq_bits = 0x02; 106362306a36Sopenharmony_ci break; 106462306a36Sopenharmony_ci case 10: 106562306a36Sopenharmony_ci irq_bits = 0x03; 106662306a36Sopenharmony_ci break; 106762306a36Sopenharmony_ci case 11: 106862306a36Sopenharmony_ci irq_bits = 0x04; 106962306a36Sopenharmony_ci break; 107062306a36Sopenharmony_ci default: 107162306a36Sopenharmony_ci snd_printk(KERN_ERR "WSS irq # %d not valid\n", chip->irq); 107262306a36Sopenharmony_ci goto __skip_resources; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci switch (chip->dma1) { 107662306a36Sopenharmony_ci case 0: 107762306a36Sopenharmony_ci dma_bits = 0x01; 107862306a36Sopenharmony_ci break; 107962306a36Sopenharmony_ci case 1: 108062306a36Sopenharmony_ci dma_bits = 0x02; 108162306a36Sopenharmony_ci break; 108262306a36Sopenharmony_ci case 3: 108362306a36Sopenharmony_ci dma_bits = 0x03; 108462306a36Sopenharmony_ci break; 108562306a36Sopenharmony_ci default: 108662306a36Sopenharmony_ci snd_printk(KERN_ERR "WSS dma1 # %d not valid\n", chip->dma1); 108762306a36Sopenharmony_ci goto __skip_resources; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (chip->dma1 == chip->dma2) { 109162306a36Sopenharmony_ci snd_printk(KERN_ERR "don't want to share dmas\n"); 109262306a36Sopenharmony_ci return -EBUSY; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci switch (chip->dma2) { 109662306a36Sopenharmony_ci case 0: 109762306a36Sopenharmony_ci case 1: 109862306a36Sopenharmony_ci break; 109962306a36Sopenharmony_ci default: 110062306a36Sopenharmony_ci snd_printk(KERN_ERR "WSS dma2 # %d not valid\n", chip->dma2); 110162306a36Sopenharmony_ci goto __skip_resources; 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci dma_bits |= 0x04; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 110662306a36Sopenharmony_ci outb(irq_bits << 3 | dma_bits, chip->wss_base); 110762306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci__skip_resources: 111062306a36Sopenharmony_ci if (chip->hardware > OPTi9XX_HW_82C928) { 111162306a36Sopenharmony_ci switch (chip->mpu_port) { 111262306a36Sopenharmony_ci case 0: 111362306a36Sopenharmony_ci case -1: 111462306a36Sopenharmony_ci break; 111562306a36Sopenharmony_ci case 0x300: 111662306a36Sopenharmony_ci mpu_port_bits = 0x03; 111762306a36Sopenharmony_ci break; 111862306a36Sopenharmony_ci case 0x310: 111962306a36Sopenharmony_ci mpu_port_bits = 0x02; 112062306a36Sopenharmony_ci break; 112162306a36Sopenharmony_ci case 0x320: 112262306a36Sopenharmony_ci mpu_port_bits = 0x01; 112362306a36Sopenharmony_ci break; 112462306a36Sopenharmony_ci case 0x330: 112562306a36Sopenharmony_ci mpu_port_bits = 0x00; 112662306a36Sopenharmony_ci break; 112762306a36Sopenharmony_ci default: 112862306a36Sopenharmony_ci snd_printk(KERN_ERR "MPU-401 port 0x%lx not valid\n", 112962306a36Sopenharmony_ci chip->mpu_port); 113062306a36Sopenharmony_ci goto __skip_mpu; 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci switch (chip->mpu_irq) { 113462306a36Sopenharmony_ci case 5: 113562306a36Sopenharmony_ci mpu_irq_bits = 0x02; 113662306a36Sopenharmony_ci break; 113762306a36Sopenharmony_ci case 7: 113862306a36Sopenharmony_ci mpu_irq_bits = 0x03; 113962306a36Sopenharmony_ci break; 114062306a36Sopenharmony_ci case 9: 114162306a36Sopenharmony_ci mpu_irq_bits = 0x00; 114262306a36Sopenharmony_ci break; 114362306a36Sopenharmony_ci case 10: 114462306a36Sopenharmony_ci mpu_irq_bits = 0x01; 114562306a36Sopenharmony_ci break; 114662306a36Sopenharmony_ci default: 114762306a36Sopenharmony_ci snd_printk(KERN_ERR "MPU-401 irq # %d not valid\n", 114862306a36Sopenharmony_ci chip->mpu_irq); 114962306a36Sopenharmony_ci goto __skip_mpu; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(6), 115362306a36Sopenharmony_ci (chip->mpu_port <= 0) ? 0x00 : 115462306a36Sopenharmony_ci 0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3, 115562306a36Sopenharmony_ci 0xf8); 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci__skip_mpu: 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci return 0; 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_cistatic int snd_miro_opti_check(struct snd_card *card, struct snd_miro *chip) 116362306a36Sopenharmony_ci{ 116462306a36Sopenharmony_ci unsigned char value; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci chip->res_mc_base = 116762306a36Sopenharmony_ci devm_request_region(card->dev, chip->mc_base, 116862306a36Sopenharmony_ci chip->mc_base_size, "OPTi9xx MC"); 116962306a36Sopenharmony_ci if (chip->res_mc_base == NULL) 117062306a36Sopenharmony_ci return -ENOMEM; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci value = snd_miro_read(chip, OPTi9XX_MC_REG(1)); 117362306a36Sopenharmony_ci if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1))) 117462306a36Sopenharmony_ci if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1))) 117562306a36Sopenharmony_ci return 0; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci devm_release_resource(card->dev, chip->res_mc_base); 117862306a36Sopenharmony_ci chip->res_mc_base = NULL; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci return -ENODEV; 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cistatic int snd_card_miro_detect(struct snd_card *card, 118462306a36Sopenharmony_ci struct snd_miro *chip) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci int i, err; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci for (i = OPTi9XX_HW_82C929; i <= OPTi9XX_HW_82C924; i++) { 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci err = snd_miro_init(chip, i); 119162306a36Sopenharmony_ci if (err < 0) 119262306a36Sopenharmony_ci return err; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci err = snd_miro_opti_check(card, chip); 119562306a36Sopenharmony_ci if (err == 0) 119662306a36Sopenharmony_ci return 1; 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci return -ENODEV; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistatic int snd_card_miro_aci_detect(struct snd_card *card, 120362306a36Sopenharmony_ci struct snd_miro *miro) 120462306a36Sopenharmony_ci{ 120562306a36Sopenharmony_ci unsigned char regval; 120662306a36Sopenharmony_ci int i; 120762306a36Sopenharmony_ci struct snd_miro_aci *aci = &aci_device; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci miro->aci = aci; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci mutex_init(&aci->aci_mutex); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci /* get ACI port from OPTi9xx MC 4 */ 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci regval=inb(miro->mc_base + 4); 121662306a36Sopenharmony_ci aci->aci_port = (regval & 0x10) ? 0x344 : 0x354; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci miro->res_aci_port = 121962306a36Sopenharmony_ci devm_request_region(card->dev, aci->aci_port, 3, "miro aci"); 122062306a36Sopenharmony_ci if (miro->res_aci_port == NULL) { 122162306a36Sopenharmony_ci snd_printk(KERN_ERR "aci i/o area 0x%lx-0x%lx already used.\n", 122262306a36Sopenharmony_ci aci->aci_port, aci->aci_port+2); 122362306a36Sopenharmony_ci return -ENOMEM; 122462306a36Sopenharmony_ci } 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci /* force ACI into a known state */ 122762306a36Sopenharmony_ci for (i = 0; i < 3; i++) 122862306a36Sopenharmony_ci if (snd_aci_cmd(aci, ACI_ERROR_OP, -1, -1) < 0) { 122962306a36Sopenharmony_ci snd_printk(KERN_ERR "can't force aci into known state.\n"); 123062306a36Sopenharmony_ci return -ENXIO; 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci aci->aci_vendor = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1); 123462306a36Sopenharmony_ci aci->aci_product = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1); 123562306a36Sopenharmony_ci if (aci->aci_vendor < 0 || aci->aci_product < 0) { 123662306a36Sopenharmony_ci snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n", 123762306a36Sopenharmony_ci aci->aci_port); 123862306a36Sopenharmony_ci return -ENXIO; 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci aci->aci_version = snd_aci_cmd(aci, ACI_READ_VERSION, -1, -1); 124262306a36Sopenharmony_ci if (aci->aci_version < 0) { 124362306a36Sopenharmony_ci snd_printk(KERN_ERR "can't read aci version on 0x%lx.\n", 124462306a36Sopenharmony_ci aci->aci_port); 124562306a36Sopenharmony_ci return -ENXIO; 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci if (snd_aci_cmd(aci, ACI_INIT, -1, -1) < 0 || 124962306a36Sopenharmony_ci snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 || 125062306a36Sopenharmony_ci snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) { 125162306a36Sopenharmony_ci snd_printk(KERN_ERR "can't initialize aci.\n"); 125262306a36Sopenharmony_ci return -ENXIO; 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci return 0; 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic int snd_miro_probe(struct snd_card *card) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci int error; 126162306a36Sopenharmony_ci struct snd_miro *miro = card->private_data; 126262306a36Sopenharmony_ci struct snd_wss *codec; 126362306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci if (!miro->res_mc_base) { 126662306a36Sopenharmony_ci miro->res_mc_base = devm_request_region(card->dev, 126762306a36Sopenharmony_ci miro->mc_base, 126862306a36Sopenharmony_ci miro->mc_base_size, 126962306a36Sopenharmony_ci "miro (OPTi9xx MC)"); 127062306a36Sopenharmony_ci if (miro->res_mc_base == NULL) { 127162306a36Sopenharmony_ci snd_printk(KERN_ERR "request for OPTI9xx MC failed\n"); 127262306a36Sopenharmony_ci return -ENOMEM; 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci error = snd_card_miro_aci_detect(card, miro); 127762306a36Sopenharmony_ci if (error < 0) { 127862306a36Sopenharmony_ci snd_printk(KERN_ERR "unable to detect aci chip\n"); 127962306a36Sopenharmony_ci return -ENODEV; 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci miro->wss_base = port; 128362306a36Sopenharmony_ci miro->mpu_port = mpu_port; 128462306a36Sopenharmony_ci miro->irq = irq; 128562306a36Sopenharmony_ci miro->mpu_irq = mpu_irq; 128662306a36Sopenharmony_ci miro->dma1 = dma1; 128762306a36Sopenharmony_ci miro->dma2 = dma2; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci /* init proc interface */ 129062306a36Sopenharmony_ci snd_miro_proc_init(card, miro); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci error = snd_miro_configure(miro); 129362306a36Sopenharmony_ci if (error) 129462306a36Sopenharmony_ci return error; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci error = snd_wss_create(card, miro->wss_base + 4, -1, 129762306a36Sopenharmony_ci miro->irq, miro->dma1, miro->dma2, 129862306a36Sopenharmony_ci WSS_HW_DETECT, 0, &codec); 129962306a36Sopenharmony_ci if (error < 0) 130062306a36Sopenharmony_ci return error; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci error = snd_wss_pcm(codec, 0); 130362306a36Sopenharmony_ci if (error < 0) 130462306a36Sopenharmony_ci return error; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci error = snd_wss_mixer(codec); 130762306a36Sopenharmony_ci if (error < 0) 130862306a36Sopenharmony_ci return error; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci error = snd_wss_timer(codec, 0); 131162306a36Sopenharmony_ci if (error < 0) 131262306a36Sopenharmony_ci return error; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci miro->pcm = codec->pcm; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci error = snd_miro_mixer(card, miro); 131762306a36Sopenharmony_ci if (error < 0) 131862306a36Sopenharmony_ci return error; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci if (miro->aci->aci_vendor == 'm') { 132162306a36Sopenharmony_ci /* It looks like a miro sound card. */ 132262306a36Sopenharmony_ci switch (miro->aci->aci_product) { 132362306a36Sopenharmony_ci case 'A': 132462306a36Sopenharmony_ci sprintf(card->shortname, 132562306a36Sopenharmony_ci "miroSOUND PCM1 pro / PCM12"); 132662306a36Sopenharmony_ci break; 132762306a36Sopenharmony_ci case 'B': 132862306a36Sopenharmony_ci sprintf(card->shortname, 132962306a36Sopenharmony_ci "miroSOUND PCM12"); 133062306a36Sopenharmony_ci break; 133162306a36Sopenharmony_ci case 'C': 133262306a36Sopenharmony_ci sprintf(card->shortname, 133362306a36Sopenharmony_ci "miroSOUND PCM20 radio"); 133462306a36Sopenharmony_ci break; 133562306a36Sopenharmony_ci default: 133662306a36Sopenharmony_ci sprintf(card->shortname, 133762306a36Sopenharmony_ci "unknown miro"); 133862306a36Sopenharmony_ci snd_printk(KERN_INFO "unknown miro aci id\n"); 133962306a36Sopenharmony_ci break; 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci } else { 134262306a36Sopenharmony_ci snd_printk(KERN_INFO "found unsupported aci card\n"); 134362306a36Sopenharmony_ci sprintf(card->shortname, "unknown Cardinal Technologies"); 134462306a36Sopenharmony_ci } 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci strcpy(card->driver, "miro"); 134762306a36Sopenharmony_ci scnprintf(card->longname, sizeof(card->longname), 134862306a36Sopenharmony_ci "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d", 134962306a36Sopenharmony_ci card->shortname, miro->name, codec->pcm->name, 135062306a36Sopenharmony_ci miro->wss_base + 4, miro->irq, miro->dma1, miro->dma2); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT) 135362306a36Sopenharmony_ci rmidi = NULL; 135462306a36Sopenharmony_ci else { 135562306a36Sopenharmony_ci error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, 135662306a36Sopenharmony_ci mpu_port, 0, miro->mpu_irq, &rmidi); 135762306a36Sopenharmony_ci if (error < 0) 135862306a36Sopenharmony_ci snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", 135962306a36Sopenharmony_ci mpu_port); 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) { 136362306a36Sopenharmony_ci struct snd_opl3 *opl3 = NULL; 136462306a36Sopenharmony_ci struct snd_opl4 *opl4; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci if (snd_opl4_create(card, fm_port, fm_port - 8, 136762306a36Sopenharmony_ci 2, &opl3, &opl4) < 0) 136862306a36Sopenharmony_ci snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n", 136962306a36Sopenharmony_ci fm_port); 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci error = snd_set_aci_init_values(miro); 137362306a36Sopenharmony_ci if (error < 0) 137462306a36Sopenharmony_ci return error; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci return snd_card_register(card); 137762306a36Sopenharmony_ci} 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_cistatic int snd_miro_isa_match(struct device *devptr, unsigned int n) 138062306a36Sopenharmony_ci{ 138162306a36Sopenharmony_ci#ifdef CONFIG_PNP 138262306a36Sopenharmony_ci if (snd_miro_pnp_is_probed) 138362306a36Sopenharmony_ci return 0; 138462306a36Sopenharmony_ci if (isapnp) 138562306a36Sopenharmony_ci return 0; 138662306a36Sopenharmony_ci#endif 138762306a36Sopenharmony_ci return 1; 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_cistatic int snd_miro_isa_probe(struct device *devptr, unsigned int n) 139162306a36Sopenharmony_ci{ 139262306a36Sopenharmony_ci static const long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1}; 139362306a36Sopenharmony_ci static const long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1}; 139462306a36Sopenharmony_ci static const int possible_irqs[] = {11, 9, 10, 7, -1}; 139562306a36Sopenharmony_ci static const int possible_mpu_irqs[] = {10, 5, 9, 7, -1}; 139662306a36Sopenharmony_ci static const int possible_dma1s[] = {3, 1, 0, -1}; 139762306a36Sopenharmony_ci static const int possible_dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1}, 139862306a36Sopenharmony_ci {0, -1} }; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci int error; 140162306a36Sopenharmony_ci struct snd_miro *miro; 140262306a36Sopenharmony_ci struct snd_card *card; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci error = snd_devm_card_new(devptr, index, id, THIS_MODULE, 140562306a36Sopenharmony_ci sizeof(struct snd_miro), &card); 140662306a36Sopenharmony_ci if (error < 0) 140762306a36Sopenharmony_ci return error; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci miro = card->private_data; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci error = snd_card_miro_detect(card, miro); 141262306a36Sopenharmony_ci if (error < 0) { 141362306a36Sopenharmony_ci snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n"); 141462306a36Sopenharmony_ci return -ENODEV; 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci if (port == SNDRV_AUTO_PORT) { 141862306a36Sopenharmony_ci port = snd_legacy_find_free_ioport(possible_ports, 4); 141962306a36Sopenharmony_ci if (port < 0) { 142062306a36Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free WSS port\n"); 142162306a36Sopenharmony_ci return -EBUSY; 142262306a36Sopenharmony_ci } 142362306a36Sopenharmony_ci } 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci if (mpu_port == SNDRV_AUTO_PORT) { 142662306a36Sopenharmony_ci mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2); 142762306a36Sopenharmony_ci if (mpu_port < 0) { 142862306a36Sopenharmony_ci snd_printk(KERN_ERR 142962306a36Sopenharmony_ci "unable to find a free MPU401 port\n"); 143062306a36Sopenharmony_ci return -EBUSY; 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ci } 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci if (irq == SNDRV_AUTO_IRQ) { 143562306a36Sopenharmony_ci irq = snd_legacy_find_free_irq(possible_irqs); 143662306a36Sopenharmony_ci if (irq < 0) { 143762306a36Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free IRQ\n"); 143862306a36Sopenharmony_ci return -EBUSY; 143962306a36Sopenharmony_ci } 144062306a36Sopenharmony_ci } 144162306a36Sopenharmony_ci if (mpu_irq == SNDRV_AUTO_IRQ) { 144262306a36Sopenharmony_ci mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs); 144362306a36Sopenharmony_ci if (mpu_irq < 0) { 144462306a36Sopenharmony_ci snd_printk(KERN_ERR 144562306a36Sopenharmony_ci "unable to find a free MPU401 IRQ\n"); 144662306a36Sopenharmony_ci return -EBUSY; 144762306a36Sopenharmony_ci } 144862306a36Sopenharmony_ci } 144962306a36Sopenharmony_ci if (dma1 == SNDRV_AUTO_DMA) { 145062306a36Sopenharmony_ci dma1 = snd_legacy_find_free_dma(possible_dma1s); 145162306a36Sopenharmony_ci if (dma1 < 0) { 145262306a36Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free DMA1\n"); 145362306a36Sopenharmony_ci return -EBUSY; 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci if (dma2 == SNDRV_AUTO_DMA) { 145762306a36Sopenharmony_ci dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]); 145862306a36Sopenharmony_ci if (dma2 < 0) { 145962306a36Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free DMA2\n"); 146062306a36Sopenharmony_ci return -EBUSY; 146162306a36Sopenharmony_ci } 146262306a36Sopenharmony_ci } 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci error = snd_miro_probe(card); 146562306a36Sopenharmony_ci if (error < 0) 146662306a36Sopenharmony_ci return error; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci dev_set_drvdata(devptr, card); 146962306a36Sopenharmony_ci return 0; 147062306a36Sopenharmony_ci} 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci#define DEV_NAME "miro" 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_cistatic struct isa_driver snd_miro_driver = { 147562306a36Sopenharmony_ci .match = snd_miro_isa_match, 147662306a36Sopenharmony_ci .probe = snd_miro_isa_probe, 147762306a36Sopenharmony_ci /* FIXME: suspend/resume */ 147862306a36Sopenharmony_ci .driver = { 147962306a36Sopenharmony_ci .name = DEV_NAME 148062306a36Sopenharmony_ci }, 148162306a36Sopenharmony_ci}; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci#ifdef CONFIG_PNP 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_cistatic int snd_card_miro_pnp(struct snd_miro *chip, 148662306a36Sopenharmony_ci struct pnp_card_link *card, 148762306a36Sopenharmony_ci const struct pnp_card_device_id *pid) 148862306a36Sopenharmony_ci{ 148962306a36Sopenharmony_ci struct pnp_dev *pdev; 149062306a36Sopenharmony_ci int err; 149162306a36Sopenharmony_ci struct pnp_dev *devmpu; 149262306a36Sopenharmony_ci struct pnp_dev *devmc; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci pdev = pnp_request_card_device(card, pid->devs[0].id, NULL); 149562306a36Sopenharmony_ci if (pdev == NULL) 149662306a36Sopenharmony_ci return -EBUSY; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL); 149962306a36Sopenharmony_ci if (devmpu == NULL) 150062306a36Sopenharmony_ci return -EBUSY; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci devmc = pnp_request_card_device(card, pid->devs[2].id, NULL); 150362306a36Sopenharmony_ci if (devmc == NULL) 150462306a36Sopenharmony_ci return -EBUSY; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci err = pnp_activate_dev(pdev); 150762306a36Sopenharmony_ci if (err < 0) { 150862306a36Sopenharmony_ci snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err); 150962306a36Sopenharmony_ci return err; 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci err = pnp_activate_dev(devmc); 151362306a36Sopenharmony_ci if (err < 0) { 151462306a36Sopenharmony_ci snd_printk(KERN_ERR "MC pnp configure failure: %d\n", 151562306a36Sopenharmony_ci err); 151662306a36Sopenharmony_ci return err; 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci port = pnp_port_start(pdev, 1); 152062306a36Sopenharmony_ci fm_port = pnp_port_start(pdev, 2) + 8; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci /* 152362306a36Sopenharmony_ci * The MC(0) is never accessed and the miroSOUND PCM20 card does not 152462306a36Sopenharmony_ci * include it in the PnP resource range. OPTI93x include it. 152562306a36Sopenharmony_ci */ 152662306a36Sopenharmony_ci chip->mc_base = pnp_port_start(devmc, 0) - 1; 152762306a36Sopenharmony_ci chip->mc_base_size = pnp_port_len(devmc, 0) + 1; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci irq = pnp_irq(pdev, 0); 153062306a36Sopenharmony_ci dma1 = pnp_dma(pdev, 0); 153162306a36Sopenharmony_ci dma2 = pnp_dma(pdev, 1); 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci if (mpu_port > 0) { 153462306a36Sopenharmony_ci err = pnp_activate_dev(devmpu); 153562306a36Sopenharmony_ci if (err < 0) { 153662306a36Sopenharmony_ci snd_printk(KERN_ERR "MPU401 pnp configure failure\n"); 153762306a36Sopenharmony_ci mpu_port = -1; 153862306a36Sopenharmony_ci return err; 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci mpu_port = pnp_port_start(devmpu, 0); 154162306a36Sopenharmony_ci mpu_irq = pnp_irq(devmpu, 0); 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci return 0; 154462306a36Sopenharmony_ci} 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_cistatic int snd_miro_pnp_probe(struct pnp_card_link *pcard, 154762306a36Sopenharmony_ci const struct pnp_card_device_id *pid) 154862306a36Sopenharmony_ci{ 154962306a36Sopenharmony_ci struct snd_card *card; 155062306a36Sopenharmony_ci int err; 155162306a36Sopenharmony_ci struct snd_miro *miro; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci if (snd_miro_pnp_is_probed) 155462306a36Sopenharmony_ci return -EBUSY; 155562306a36Sopenharmony_ci if (!isapnp) 155662306a36Sopenharmony_ci return -ENODEV; 155762306a36Sopenharmony_ci err = snd_devm_card_new(&pcard->card->dev, index, id, THIS_MODULE, 155862306a36Sopenharmony_ci sizeof(struct snd_miro), &card); 155962306a36Sopenharmony_ci if (err < 0) 156062306a36Sopenharmony_ci return err; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci miro = card->private_data; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci err = snd_card_miro_pnp(miro, pcard, pid); 156562306a36Sopenharmony_ci if (err) 156662306a36Sopenharmony_ci return err; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci /* only miroSOUND PCM20 and PCM12 == OPTi924 */ 156962306a36Sopenharmony_ci err = snd_miro_init(miro, OPTi9XX_HW_82C924); 157062306a36Sopenharmony_ci if (err) 157162306a36Sopenharmony_ci return err; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci err = snd_miro_opti_check(card, miro); 157462306a36Sopenharmony_ci if (err) { 157562306a36Sopenharmony_ci snd_printk(KERN_ERR "OPTI chip not found\n"); 157662306a36Sopenharmony_ci return err; 157762306a36Sopenharmony_ci } 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci err = snd_miro_probe(card); 158062306a36Sopenharmony_ci if (err < 0) 158162306a36Sopenharmony_ci return err; 158262306a36Sopenharmony_ci pnp_set_card_drvdata(pcard, card); 158362306a36Sopenharmony_ci snd_miro_pnp_is_probed = 1; 158462306a36Sopenharmony_ci return 0; 158562306a36Sopenharmony_ci} 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_cistatic void snd_miro_pnp_remove(struct pnp_card_link *pcard) 158862306a36Sopenharmony_ci{ 158962306a36Sopenharmony_ci snd_miro_pnp_is_probed = 0; 159062306a36Sopenharmony_ci} 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_cistatic struct pnp_card_driver miro_pnpc_driver = { 159362306a36Sopenharmony_ci .flags = PNP_DRIVER_RES_DISABLE, 159462306a36Sopenharmony_ci .name = "miro", 159562306a36Sopenharmony_ci .id_table = snd_miro_pnpids, 159662306a36Sopenharmony_ci .probe = snd_miro_pnp_probe, 159762306a36Sopenharmony_ci .remove = snd_miro_pnp_remove, 159862306a36Sopenharmony_ci}; 159962306a36Sopenharmony_ci#endif 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_cistatic int __init alsa_card_miro_init(void) 160262306a36Sopenharmony_ci{ 160362306a36Sopenharmony_ci#ifdef CONFIG_PNP 160462306a36Sopenharmony_ci pnp_register_card_driver(&miro_pnpc_driver); 160562306a36Sopenharmony_ci if (snd_miro_pnp_is_probed) 160662306a36Sopenharmony_ci return 0; 160762306a36Sopenharmony_ci pnp_unregister_card_driver(&miro_pnpc_driver); 160862306a36Sopenharmony_ci#endif 160962306a36Sopenharmony_ci return isa_register_driver(&snd_miro_driver, 1); 161062306a36Sopenharmony_ci} 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_cistatic void __exit alsa_card_miro_exit(void) 161362306a36Sopenharmony_ci{ 161462306a36Sopenharmony_ci if (!snd_miro_pnp_is_probed) { 161562306a36Sopenharmony_ci isa_unregister_driver(&snd_miro_driver); 161662306a36Sopenharmony_ci return; 161762306a36Sopenharmony_ci } 161862306a36Sopenharmony_ci#ifdef CONFIG_PNP 161962306a36Sopenharmony_ci pnp_unregister_card_driver(&miro_pnpc_driver); 162062306a36Sopenharmony_ci#endif 162162306a36Sopenharmony_ci} 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_cimodule_init(alsa_card_miro_init) 162462306a36Sopenharmony_cimodule_exit(alsa_card_miro_exit) 1625