18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ALSA soundcard driver for Miro miroSOUND PCM1 pro 48c2ecf20Sopenharmony_ci * miroSOUND PCM12 58c2ecf20Sopenharmony_ci * miroSOUND PCM20 Radio 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2004-2005 Martin Langer <martin-langer@gmx.de> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on OSS ACI and ALSA OPTi9xx drivers 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/isa.h> 158c2ecf20Sopenharmony_ci#include <linux/pnp.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/ioport.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <asm/dma.h> 218c2ecf20Sopenharmony_ci#include <sound/core.h> 228c2ecf20Sopenharmony_ci#include <sound/wss.h> 238c2ecf20Sopenharmony_ci#include <sound/mpu401.h> 248c2ecf20Sopenharmony_ci#include <sound/opl4.h> 258c2ecf20Sopenharmony_ci#include <sound/control.h> 268c2ecf20Sopenharmony_ci#include <sound/info.h> 278c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IOPORT 288c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IRQ 298c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_DMA 308c2ecf20Sopenharmony_ci#include <sound/initval.h> 318c2ecf20Sopenharmony_ci#include <sound/aci.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>"); 348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Miro miroSOUND PCM1 pro, PCM12, PCM20 Radio"); 368c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Miro,miroSOUND PCM1 pro}, " 378c2ecf20Sopenharmony_ci "{Miro,miroSOUND PCM12}, " 388c2ecf20Sopenharmony_ci "{Miro,miroSOUND PCM20 Radio}}"); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ 418c2ecf20Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ 428c2ecf20Sopenharmony_cistatic long port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */ 438c2ecf20Sopenharmony_cistatic long mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */ 448c2ecf20Sopenharmony_cistatic long fm_port = SNDRV_DEFAULT_PORT1; /* 0x388 */ 458c2ecf20Sopenharmony_cistatic int irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10,11 */ 468c2ecf20Sopenharmony_cistatic int mpu_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10 */ 478c2ecf20Sopenharmony_cistatic int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ 488c2ecf20Sopenharmony_cistatic int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ 498c2ecf20Sopenharmony_cistatic int wss; 508c2ecf20Sopenharmony_cistatic int ide; 518c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 528c2ecf20Sopenharmony_cistatic bool isapnp = 1; /* Enable ISA PnP detection */ 538c2ecf20Sopenharmony_ci#endif 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cimodule_param(index, int, 0444); 568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for miro soundcard."); 578c2ecf20Sopenharmony_cimodule_param(id, charp, 0444); 588c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for miro soundcard."); 598c2ecf20Sopenharmony_cimodule_param_hw(port, long, ioport, 0444); 608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(port, "WSS port # for miro driver."); 618c2ecf20Sopenharmony_cimodule_param_hw(mpu_port, long, ioport, 0444); 628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mpu_port, "MPU-401 port # for miro driver."); 638c2ecf20Sopenharmony_cimodule_param_hw(fm_port, long, ioport, 0444); 648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fm_port, "FM Port # for miro driver."); 658c2ecf20Sopenharmony_cimodule_param_hw(irq, int, irq, 0444); 668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "WSS irq # for miro driver."); 678c2ecf20Sopenharmony_cimodule_param_hw(mpu_irq, int, irq, 0444); 688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for miro driver."); 698c2ecf20Sopenharmony_cimodule_param_hw(dma1, int, dma, 0444); 708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma1, "1st dma # for miro driver."); 718c2ecf20Sopenharmony_cimodule_param_hw(dma2, int, dma, 0444); 728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma2, "2nd dma # for miro driver."); 738c2ecf20Sopenharmony_cimodule_param(wss, int, 0444); 748c2ecf20Sopenharmony_ciMODULE_PARM_DESC(wss, "wss mode"); 758c2ecf20Sopenharmony_cimodule_param(ide, int, 0444); 768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ide, "enable ide port"); 778c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 788c2ecf20Sopenharmony_cimodule_param(isapnp, bool, 0444); 798c2ecf20Sopenharmony_ciMODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard."); 808c2ecf20Sopenharmony_ci#endif 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define OPTi9XX_HW_DETECT 0 838c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C928 1 848c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C929 2 858c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C924 3 868c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C925 4 878c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C930 5 888c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C931 6 898c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C933 7 908c2ecf20Sopenharmony_ci#define OPTi9XX_HW_LAST OPTi9XX_HW_82C933 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define OPTi9XX_MC_REG(n) n 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistruct snd_miro { 958c2ecf20Sopenharmony_ci unsigned short hardware; 968c2ecf20Sopenharmony_ci unsigned char password; 978c2ecf20Sopenharmony_ci char name[7]; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci struct resource *res_mc_base; 1008c2ecf20Sopenharmony_ci struct resource *res_aci_port; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci unsigned long mc_base; 1038c2ecf20Sopenharmony_ci unsigned long mc_base_size; 1048c2ecf20Sopenharmony_ci unsigned long pwd_reg; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci spinlock_t lock; 1078c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci long wss_base; 1108c2ecf20Sopenharmony_ci int irq; 1118c2ecf20Sopenharmony_ci int dma1; 1128c2ecf20Sopenharmony_ci int dma2; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci long mpu_port; 1158c2ecf20Sopenharmony_ci int mpu_irq; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci struct snd_miro_aci *aci; 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic struct snd_miro_aci aci_device; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic const char * const snd_opti9xx_names[] = { 1238c2ecf20Sopenharmony_ci "unknown", 1248c2ecf20Sopenharmony_ci "82C928", "82C929", 1258c2ecf20Sopenharmony_ci "82C924", "82C925", 1268c2ecf20Sopenharmony_ci "82C930", "82C931", "82C933" 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int snd_miro_pnp_is_probed; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic const struct pnp_card_device_id snd_miro_pnpids[] = { 1348c2ecf20Sopenharmony_ci /* PCM20 and PCM12 in PnP mode */ 1358c2ecf20Sopenharmony_ci { .id = "MIR0924", 1368c2ecf20Sopenharmony_ci .devs = { { "MIR0000" }, { "MIR0002" }, { "MIR0005" } }, }, 1378c2ecf20Sopenharmony_ci { .id = "" } 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_miro_pnpids); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci#endif /* CONFIG_PNP */ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* 1458c2ecf20Sopenharmony_ci * ACI control 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int aci_busy_wait(struct snd_miro_aci *aci) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci long timeout; 1518c2ecf20Sopenharmony_ci unsigned char byte; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci for (timeout = 1; timeout <= ACI_MINTIME + 30; timeout++) { 1548c2ecf20Sopenharmony_ci byte = inb(aci->aci_port + ACI_REG_BUSY); 1558c2ecf20Sopenharmony_ci if ((byte & 1) == 0) { 1568c2ecf20Sopenharmony_ci if (timeout >= ACI_MINTIME) 1578c2ecf20Sopenharmony_ci snd_printd("aci ready in round %ld.\n", 1588c2ecf20Sopenharmony_ci timeout-ACI_MINTIME); 1598c2ecf20Sopenharmony_ci return byte; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci if (timeout >= ACI_MINTIME) { 1628c2ecf20Sopenharmony_ci long out=10*HZ; 1638c2ecf20Sopenharmony_ci switch (timeout-ACI_MINTIME) { 1648c2ecf20Sopenharmony_ci case 0 ... 9: 1658c2ecf20Sopenharmony_ci out /= 10; 1668c2ecf20Sopenharmony_ci fallthrough; 1678c2ecf20Sopenharmony_ci case 10 ... 19: 1688c2ecf20Sopenharmony_ci out /= 10; 1698c2ecf20Sopenharmony_ci fallthrough; 1708c2ecf20Sopenharmony_ci case 20 ... 30: 1718c2ecf20Sopenharmony_ci out /= 10; 1728c2ecf20Sopenharmony_ci fallthrough; 1738c2ecf20Sopenharmony_ci default: 1748c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 1758c2ecf20Sopenharmony_ci schedule_timeout(out); 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci_busy_wait() time out\n"); 1818c2ecf20Sopenharmony_ci return -EBUSY; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic inline int aci_write(struct snd_miro_aci *aci, unsigned char byte) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci if (aci_busy_wait(aci) >= 0) { 1878c2ecf20Sopenharmony_ci outb(byte, aci->aci_port + ACI_REG_COMMAND); 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci } else { 1908c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci busy, aci_write(0x%x) stopped.\n", byte); 1918c2ecf20Sopenharmony_ci return -EBUSY; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic inline int aci_read(struct snd_miro_aci *aci) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci unsigned char byte; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (aci_busy_wait(aci) >= 0) { 2008c2ecf20Sopenharmony_ci byte = inb(aci->aci_port + ACI_REG_STATUS); 2018c2ecf20Sopenharmony_ci return byte; 2028c2ecf20Sopenharmony_ci } else { 2038c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci busy, aci_read() stopped.\n"); 2048c2ecf20Sopenharmony_ci return -EBUSY; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ciint snd_aci_cmd(struct snd_miro_aci *aci, int write1, int write2, int write3) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci int write[] = {write1, write2, write3}; 2118c2ecf20Sopenharmony_ci int value, i; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&aci->aci_mutex)) 2148c2ecf20Sopenharmony_ci return -EINTR; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci for (i=0; i<3; i++) { 2178c2ecf20Sopenharmony_ci if (write[i]< 0 || write[i] > 255) 2188c2ecf20Sopenharmony_ci break; 2198c2ecf20Sopenharmony_ci else { 2208c2ecf20Sopenharmony_ci value = aci_write(aci, write[i]); 2218c2ecf20Sopenharmony_ci if (value < 0) 2228c2ecf20Sopenharmony_ci goto out; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci value = aci_read(aci); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ciout: mutex_unlock(&aci->aci_mutex); 2298c2ecf20Sopenharmony_ci return value; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_aci_cmd); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int aci_getvalue(struct snd_miro_aci *aci, unsigned char index) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci return snd_aci_cmd(aci, ACI_STATUS, index, -1); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int aci_setvalue(struct snd_miro_aci *aci, unsigned char index, 2398c2ecf20Sopenharmony_ci int value) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci return snd_aci_cmd(aci, index, value, -1); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistruct snd_miro_aci *snd_aci_get_aci(void) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci if (aci_device.aci_port == 0) 2478c2ecf20Sopenharmony_ci return NULL; 2488c2ecf20Sopenharmony_ci return &aci_device; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_aci_get_aci); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/* 2538c2ecf20Sopenharmony_ci * MIXER part 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci#define snd_miro_info_capture snd_ctl_boolean_mono_info 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic int snd_miro_get_capture(struct snd_kcontrol *kcontrol, 2598c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 2628c2ecf20Sopenharmony_ci int value; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci value = aci_getvalue(miro->aci, ACI_S_GENERAL); 2658c2ecf20Sopenharmony_ci if (value < 0) { 2668c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n", 2678c2ecf20Sopenharmony_ci value); 2688c2ecf20Sopenharmony_ci return value; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = value & 0x20; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int snd_miro_put_capture(struct snd_kcontrol *kcontrol, 2778c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 2808c2ecf20Sopenharmony_ci int change, value, error; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci value = !(ucontrol->value.integer.value[0]); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci error = aci_setvalue(miro->aci, ACI_SET_SOLOMODE, value); 2858c2ecf20Sopenharmony_ci if (error < 0) { 2868c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n", 2878c2ecf20Sopenharmony_ci error); 2888c2ecf20Sopenharmony_ci return error; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci change = (value != miro->aci->aci_solomode); 2928c2ecf20Sopenharmony_ci miro->aci->aci_solomode = value; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return change; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int snd_miro_info_preamp(struct snd_kcontrol *kcontrol, 2988c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 3018c2ecf20Sopenharmony_ci uinfo->count = 1; 3028c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 3038c2ecf20Sopenharmony_ci uinfo->value.integer.max = 3; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic int snd_miro_get_preamp(struct snd_kcontrol *kcontrol, 3098c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 3128c2ecf20Sopenharmony_ci int value; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (miro->aci->aci_version <= 176) { 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* 3178c2ecf20Sopenharmony_ci OSS says it's not readable with versions < 176. 3188c2ecf20Sopenharmony_ci But it doesn't work on my card, 3198c2ecf20Sopenharmony_ci which is a PCM12 with aci_version = 176. 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = miro->aci->aci_preamp; 3238c2ecf20Sopenharmony_ci return 0; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci value = aci_getvalue(miro->aci, ACI_GET_PREAMP); 3278c2ecf20Sopenharmony_ci if (value < 0) { 3288c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n", 3298c2ecf20Sopenharmony_ci value); 3308c2ecf20Sopenharmony_ci return value; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = value; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int snd_miro_put_preamp(struct snd_kcontrol *kcontrol, 3398c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 3428c2ecf20Sopenharmony_ci int error, value, change; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci value = ucontrol->value.integer.value[0]; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci error = aci_setvalue(miro->aci, ACI_SET_PREAMP, value); 3478c2ecf20Sopenharmony_ci if (error < 0) { 3488c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n", 3498c2ecf20Sopenharmony_ci error); 3508c2ecf20Sopenharmony_ci return error; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci change = (value != miro->aci->aci_preamp); 3548c2ecf20Sopenharmony_ci miro->aci->aci_preamp = value; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return change; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci#define snd_miro_info_amp snd_ctl_boolean_mono_info 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int snd_miro_get_amp(struct snd_kcontrol *kcontrol, 3628c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 3658c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = miro->aci->aci_amp; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int snd_miro_put_amp(struct snd_kcontrol *kcontrol, 3718c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 3748c2ecf20Sopenharmony_ci int error, value, change; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci value = ucontrol->value.integer.value[0]; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci error = aci_setvalue(miro->aci, ACI_SET_POWERAMP, value); 3798c2ecf20Sopenharmony_ci if (error < 0) { 3808c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "snd_miro_put_amp() to %d failed: %d\n", value, error); 3818c2ecf20Sopenharmony_ci return error; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci change = (value != miro->aci->aci_amp); 3858c2ecf20Sopenharmony_ci miro->aci->aci_amp = value; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return change; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci#define MIRO_DOUBLE(ctl_name, ctl_index, get_right_reg, set_right_reg) \ 3918c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 3928c2ecf20Sopenharmony_ci .name = ctl_name, \ 3938c2ecf20Sopenharmony_ci .index = ctl_index, \ 3948c2ecf20Sopenharmony_ci .info = snd_miro_info_double, \ 3958c2ecf20Sopenharmony_ci .get = snd_miro_get_double, \ 3968c2ecf20Sopenharmony_ci .put = snd_miro_put_double, \ 3978c2ecf20Sopenharmony_ci .private_value = get_right_reg | (set_right_reg << 8) \ 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int snd_miro_info_double(struct snd_kcontrol *kcontrol, 4018c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 4068c2ecf20Sopenharmony_ci uinfo->count = 2; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if ((reg >= ACI_GET_EQ1) && (reg <= ACI_GET_EQ7)) { 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* equalizer elements */ 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci uinfo->value.integer.min = - 0x7f; 4138c2ecf20Sopenharmony_ci uinfo->value.integer.max = 0x7f; 4148c2ecf20Sopenharmony_ci } else { 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* non-equalizer elements */ 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 4198c2ecf20Sopenharmony_ci uinfo->value.integer.max = 0x20; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic int snd_miro_get_double(struct snd_kcontrol *kcontrol, 4268c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uinfo) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 4298c2ecf20Sopenharmony_ci int left_val, right_val; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci int right_reg = kcontrol->private_value & 0xff; 4328c2ecf20Sopenharmony_ci int left_reg = right_reg + 1; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci right_val = aci_getvalue(miro->aci, right_reg); 4358c2ecf20Sopenharmony_ci if (right_val < 0) { 4368c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", right_reg, right_val); 4378c2ecf20Sopenharmony_ci return right_val; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci left_val = aci_getvalue(miro->aci, left_reg); 4418c2ecf20Sopenharmony_ci if (left_val < 0) { 4428c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", left_reg, left_val); 4438c2ecf20Sopenharmony_ci return left_val; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci if ((right_reg >= ACI_GET_EQ1) && (right_reg <= ACI_GET_EQ7)) { 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* equalizer elements */ 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (left_val < 0x80) { 4518c2ecf20Sopenharmony_ci uinfo->value.integer.value[0] = left_val; 4528c2ecf20Sopenharmony_ci } else { 4538c2ecf20Sopenharmony_ci uinfo->value.integer.value[0] = 0x80 - left_val; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (right_val < 0x80) { 4578c2ecf20Sopenharmony_ci uinfo->value.integer.value[1] = right_val; 4588c2ecf20Sopenharmony_ci } else { 4598c2ecf20Sopenharmony_ci uinfo->value.integer.value[1] = 0x80 - right_val; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci } else { 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci /* non-equalizer elements */ 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci uinfo->value.integer.value[0] = 0x20 - left_val; 4678c2ecf20Sopenharmony_ci uinfo->value.integer.value[1] = 0x20 - right_val; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int snd_miro_put_double(struct snd_kcontrol *kcontrol, 4748c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct snd_miro *miro = snd_kcontrol_chip(kcontrol); 4778c2ecf20Sopenharmony_ci struct snd_miro_aci *aci = miro->aci; 4788c2ecf20Sopenharmony_ci int left, right, left_old, right_old; 4798c2ecf20Sopenharmony_ci int setreg_left, setreg_right, getreg_left, getreg_right; 4808c2ecf20Sopenharmony_ci int change, error; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci left = ucontrol->value.integer.value[0]; 4838c2ecf20Sopenharmony_ci right = ucontrol->value.integer.value[1]; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci setreg_right = (kcontrol->private_value >> 8) & 0xff; 4868c2ecf20Sopenharmony_ci setreg_left = setreg_right + 8; 4878c2ecf20Sopenharmony_ci if (setreg_right == ACI_SET_MASTER) 4888c2ecf20Sopenharmony_ci setreg_left -= 7; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci getreg_right = kcontrol->private_value & 0xff; 4918c2ecf20Sopenharmony_ci getreg_left = getreg_right + 1; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci left_old = aci_getvalue(aci, getreg_left); 4948c2ecf20Sopenharmony_ci if (left_old < 0) { 4958c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_left, left_old); 4968c2ecf20Sopenharmony_ci return left_old; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci right_old = aci_getvalue(aci, getreg_right); 5008c2ecf20Sopenharmony_ci if (right_old < 0) { 5018c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_right, right_old); 5028c2ecf20Sopenharmony_ci return right_old; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if ((getreg_right >= ACI_GET_EQ1) && (getreg_right <= ACI_GET_EQ7)) { 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* equalizer elements */ 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (left < -0x7f || left > 0x7f || 5108c2ecf20Sopenharmony_ci right < -0x7f || right > 0x7f) 5118c2ecf20Sopenharmony_ci return -EINVAL; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (left_old > 0x80) 5148c2ecf20Sopenharmony_ci left_old = 0x80 - left_old; 5158c2ecf20Sopenharmony_ci if (right_old > 0x80) 5168c2ecf20Sopenharmony_ci right_old = 0x80 - right_old; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (left >= 0) { 5198c2ecf20Sopenharmony_ci error = aci_setvalue(aci, setreg_left, left); 5208c2ecf20Sopenharmony_ci if (error < 0) { 5218c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 5228c2ecf20Sopenharmony_ci left, error); 5238c2ecf20Sopenharmony_ci return error; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci } else { 5268c2ecf20Sopenharmony_ci error = aci_setvalue(aci, setreg_left, 0x80 - left); 5278c2ecf20Sopenharmony_ci if (error < 0) { 5288c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 5298c2ecf20Sopenharmony_ci 0x80 - left, error); 5308c2ecf20Sopenharmony_ci return error; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (right >= 0) { 5358c2ecf20Sopenharmony_ci error = aci_setvalue(aci, setreg_right, right); 5368c2ecf20Sopenharmony_ci if (error < 0) { 5378c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 5388c2ecf20Sopenharmony_ci right, error); 5398c2ecf20Sopenharmony_ci return error; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci } else { 5428c2ecf20Sopenharmony_ci error = aci_setvalue(aci, setreg_right, 0x80 - right); 5438c2ecf20Sopenharmony_ci if (error < 0) { 5448c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 5458c2ecf20Sopenharmony_ci 0x80 - right, error); 5468c2ecf20Sopenharmony_ci return error; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci } else { 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* non-equalizer elements */ 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (left < 0 || left > 0x20 || 5558c2ecf20Sopenharmony_ci right < 0 || right > 0x20) 5568c2ecf20Sopenharmony_ci return -EINVAL; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci left_old = 0x20 - left_old; 5598c2ecf20Sopenharmony_ci right_old = 0x20 - right_old; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci error = aci_setvalue(aci, setreg_left, 0x20 - left); 5628c2ecf20Sopenharmony_ci if (error < 0) { 5638c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 5648c2ecf20Sopenharmony_ci 0x20 - left, error); 5658c2ecf20Sopenharmony_ci return error; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci error = aci_setvalue(aci, setreg_right, 0x20 - right); 5688c2ecf20Sopenharmony_ci if (error < 0) { 5698c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 5708c2ecf20Sopenharmony_ci 0x20 - right, error); 5718c2ecf20Sopenharmony_ci return error; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci change = (left != left_old) || (right != right_old); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci return change; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_controls[] = { 5818c2ecf20Sopenharmony_ciMIRO_DOUBLE("Master Playback Volume", 0, ACI_GET_MASTER, ACI_SET_MASTER), 5828c2ecf20Sopenharmony_ciMIRO_DOUBLE("Mic Playback Volume", 1, ACI_GET_MIC, ACI_SET_MIC), 5838c2ecf20Sopenharmony_ciMIRO_DOUBLE("Line Playback Volume", 1, ACI_GET_LINE, ACI_SET_LINE), 5848c2ecf20Sopenharmony_ciMIRO_DOUBLE("CD Playback Volume", 0, ACI_GET_CD, ACI_SET_CD), 5858c2ecf20Sopenharmony_ciMIRO_DOUBLE("Synth Playback Volume", 0, ACI_GET_SYNTH, ACI_SET_SYNTH), 5868c2ecf20Sopenharmony_ciMIRO_DOUBLE("PCM Playback Volume", 1, ACI_GET_PCM, ACI_SET_PCM), 5878c2ecf20Sopenharmony_ciMIRO_DOUBLE("Aux Playback Volume", 2, ACI_GET_LINE2, ACI_SET_LINE2), 5888c2ecf20Sopenharmony_ci}; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci/* Equalizer with seven bands (only PCM20) 5918c2ecf20Sopenharmony_ci from -12dB up to +12dB on each band */ 5928c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_eq_controls[] = { 5938c2ecf20Sopenharmony_ciMIRO_DOUBLE("Tone Control - 28 Hz", 0, ACI_GET_EQ1, ACI_SET_EQ1), 5948c2ecf20Sopenharmony_ciMIRO_DOUBLE("Tone Control - 160 Hz", 0, ACI_GET_EQ2, ACI_SET_EQ2), 5958c2ecf20Sopenharmony_ciMIRO_DOUBLE("Tone Control - 400 Hz", 0, ACI_GET_EQ3, ACI_SET_EQ3), 5968c2ecf20Sopenharmony_ciMIRO_DOUBLE("Tone Control - 1 kHz", 0, ACI_GET_EQ4, ACI_SET_EQ4), 5978c2ecf20Sopenharmony_ciMIRO_DOUBLE("Tone Control - 2.5 kHz", 0, ACI_GET_EQ5, ACI_SET_EQ5), 5988c2ecf20Sopenharmony_ciMIRO_DOUBLE("Tone Control - 6.3 kHz", 0, ACI_GET_EQ6, ACI_SET_EQ6), 5998c2ecf20Sopenharmony_ciMIRO_DOUBLE("Tone Control - 16 kHz", 0, ACI_GET_EQ7, ACI_SET_EQ7), 6008c2ecf20Sopenharmony_ci}; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_radio_control[] = { 6038c2ecf20Sopenharmony_ciMIRO_DOUBLE("Radio Playback Volume", 0, ACI_GET_LINE1, ACI_SET_LINE1), 6048c2ecf20Sopenharmony_ci}; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_line_control[] = { 6078c2ecf20Sopenharmony_ciMIRO_DOUBLE("Line Playback Volume", 2, ACI_GET_LINE1, ACI_SET_LINE1), 6088c2ecf20Sopenharmony_ci}; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_preamp_control[] = { 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 6138c2ecf20Sopenharmony_ci .name = "Mic Boost", 6148c2ecf20Sopenharmony_ci .index = 1, 6158c2ecf20Sopenharmony_ci .info = snd_miro_info_preamp, 6168c2ecf20Sopenharmony_ci .get = snd_miro_get_preamp, 6178c2ecf20Sopenharmony_ci .put = snd_miro_put_preamp, 6188c2ecf20Sopenharmony_ci}}; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_amp_control[] = { 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 6238c2ecf20Sopenharmony_ci .name = "Line Boost", 6248c2ecf20Sopenharmony_ci .index = 0, 6258c2ecf20Sopenharmony_ci .info = snd_miro_info_amp, 6268c2ecf20Sopenharmony_ci .get = snd_miro_get_amp, 6278c2ecf20Sopenharmony_ci .put = snd_miro_put_amp, 6288c2ecf20Sopenharmony_ci}}; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_capture_control[] = { 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 6338c2ecf20Sopenharmony_ci .name = "PCM Capture Switch", 6348c2ecf20Sopenharmony_ci .index = 0, 6358c2ecf20Sopenharmony_ci .info = snd_miro_info_capture, 6368c2ecf20Sopenharmony_ci .get = snd_miro_get_capture, 6378c2ecf20Sopenharmony_ci .put = snd_miro_put_capture, 6388c2ecf20Sopenharmony_ci}}; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic const unsigned char aci_init_values[][2] = { 6418c2ecf20Sopenharmony_ci { ACI_SET_MUTE, 0x00 }, 6428c2ecf20Sopenharmony_ci { ACI_SET_POWERAMP, 0x00 }, 6438c2ecf20Sopenharmony_ci { ACI_SET_PREAMP, 0x00 }, 6448c2ecf20Sopenharmony_ci { ACI_SET_SOLOMODE, 0x00 }, 6458c2ecf20Sopenharmony_ci { ACI_SET_MIC + 0, 0x20 }, 6468c2ecf20Sopenharmony_ci { ACI_SET_MIC + 8, 0x20 }, 6478c2ecf20Sopenharmony_ci { ACI_SET_LINE + 0, 0x20 }, 6488c2ecf20Sopenharmony_ci { ACI_SET_LINE + 8, 0x20 }, 6498c2ecf20Sopenharmony_ci { ACI_SET_CD + 0, 0x20 }, 6508c2ecf20Sopenharmony_ci { ACI_SET_CD + 8, 0x20 }, 6518c2ecf20Sopenharmony_ci { ACI_SET_PCM + 0, 0x20 }, 6528c2ecf20Sopenharmony_ci { ACI_SET_PCM + 8, 0x20 }, 6538c2ecf20Sopenharmony_ci { ACI_SET_LINE1 + 0, 0x20 }, 6548c2ecf20Sopenharmony_ci { ACI_SET_LINE1 + 8, 0x20 }, 6558c2ecf20Sopenharmony_ci { ACI_SET_LINE2 + 0, 0x20 }, 6568c2ecf20Sopenharmony_ci { ACI_SET_LINE2 + 8, 0x20 }, 6578c2ecf20Sopenharmony_ci { ACI_SET_SYNTH + 0, 0x20 }, 6588c2ecf20Sopenharmony_ci { ACI_SET_SYNTH + 8, 0x20 }, 6598c2ecf20Sopenharmony_ci { ACI_SET_MASTER + 0, 0x20 }, 6608c2ecf20Sopenharmony_ci { ACI_SET_MASTER + 1, 0x20 }, 6618c2ecf20Sopenharmony_ci}; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic int snd_set_aci_init_values(struct snd_miro *miro) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci int idx, error; 6668c2ecf20Sopenharmony_ci struct snd_miro_aci *aci = miro->aci; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* enable WSS on PCM1 */ 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if ((aci->aci_product == 'A') && wss) { 6718c2ecf20Sopenharmony_ci error = aci_setvalue(aci, ACI_SET_WSS, wss); 6728c2ecf20Sopenharmony_ci if (error < 0) { 6738c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "enabling WSS mode failed\n"); 6748c2ecf20Sopenharmony_ci return error; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* enable IDE port */ 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (ide) { 6818c2ecf20Sopenharmony_ci error = aci_setvalue(aci, ACI_SET_IDE, ide); 6828c2ecf20Sopenharmony_ci if (error < 0) { 6838c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "enabling IDE port failed\n"); 6848c2ecf20Sopenharmony_ci return error; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* set common aci values */ 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(aci_init_values); idx++) { 6918c2ecf20Sopenharmony_ci error = aci_setvalue(aci, aci_init_values[idx][0], 6928c2ecf20Sopenharmony_ci aci_init_values[idx][1]); 6938c2ecf20Sopenharmony_ci if (error < 0) { 6948c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n", 6958c2ecf20Sopenharmony_ci aci_init_values[idx][0], error); 6968c2ecf20Sopenharmony_ci return error; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci aci->aci_amp = 0; 7008c2ecf20Sopenharmony_ci aci->aci_preamp = 0; 7018c2ecf20Sopenharmony_ci aci->aci_solomode = 1; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci return 0; 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic int snd_miro_mixer(struct snd_card *card, 7078c2ecf20Sopenharmony_ci struct snd_miro *miro) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci unsigned int idx; 7108c2ecf20Sopenharmony_ci int err; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci if (snd_BUG_ON(!miro || !card)) 7138c2ecf20Sopenharmony_ci return -EINVAL; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci switch (miro->hardware) { 7168c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C924: 7178c2ecf20Sopenharmony_ci strcpy(card->mixername, "ACI & OPTi924"); 7188c2ecf20Sopenharmony_ci break; 7198c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C929: 7208c2ecf20Sopenharmony_ci strcpy(card->mixername, "ACI & OPTi929"); 7218c2ecf20Sopenharmony_ci break; 7228c2ecf20Sopenharmony_ci default: 7238c2ecf20Sopenharmony_ci snd_BUG(); 7248c2ecf20Sopenharmony_ci break; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_miro_controls); idx++) { 7288c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_controls[idx], miro))) < 0) 7298c2ecf20Sopenharmony_ci return err; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if ((miro->aci->aci_product == 'A') || 7338c2ecf20Sopenharmony_ci (miro->aci->aci_product == 'B')) { 7348c2ecf20Sopenharmony_ci /* PCM1/PCM12 with power-amp and Line 2 */ 7358c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_line_control[0], miro))) < 0) 7368c2ecf20Sopenharmony_ci return err; 7378c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_amp_control[0], miro))) < 0) 7388c2ecf20Sopenharmony_ci return err; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if ((miro->aci->aci_product == 'B') || 7428c2ecf20Sopenharmony_ci (miro->aci->aci_product == 'C')) { 7438c2ecf20Sopenharmony_ci /* PCM12/PCM20 with mic-preamp */ 7448c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_preamp_control[0], miro))) < 0) 7458c2ecf20Sopenharmony_ci return err; 7468c2ecf20Sopenharmony_ci if (miro->aci->aci_version >= 176) 7478c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro))) < 0) 7488c2ecf20Sopenharmony_ci return err; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (miro->aci->aci_product == 'C') { 7528c2ecf20Sopenharmony_ci /* PCM20 with radio and 7 band equalizer */ 7538c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_radio_control[0], miro))) < 0) 7548c2ecf20Sopenharmony_ci return err; 7558c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_miro_eq_controls); idx++) { 7568c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_eq_controls[idx], miro))) < 0) 7578c2ecf20Sopenharmony_ci return err; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci return 0; 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic int snd_miro_init(struct snd_miro *chip, 7658c2ecf20Sopenharmony_ci unsigned short hardware) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2}; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci chip->hardware = hardware; 7708c2ecf20Sopenharmony_ci strcpy(chip->name, snd_opti9xx_names[hardware]); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci chip->mc_base_size = opti9xx_mc_size[hardware]; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci spin_lock_init(&chip->lock); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci chip->wss_base = -1; 7778c2ecf20Sopenharmony_ci chip->irq = -1; 7788c2ecf20Sopenharmony_ci chip->dma1 = -1; 7798c2ecf20Sopenharmony_ci chip->dma2 = -1; 7808c2ecf20Sopenharmony_ci chip->mpu_port = -1; 7818c2ecf20Sopenharmony_ci chip->mpu_irq = -1; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci chip->pwd_reg = 3; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 7868c2ecf20Sopenharmony_ci if (isapnp && chip->mc_base) 7878c2ecf20Sopenharmony_ci /* PnP resource gives the least 10 bits */ 7888c2ecf20Sopenharmony_ci chip->mc_base |= 0xc00; 7898c2ecf20Sopenharmony_ci else 7908c2ecf20Sopenharmony_ci#endif 7918c2ecf20Sopenharmony_ci chip->mc_base = 0xf8c; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci switch (hardware) { 7948c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C929: 7958c2ecf20Sopenharmony_ci chip->password = 0xe3; 7968c2ecf20Sopenharmony_ci break; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C924: 7998c2ecf20Sopenharmony_ci chip->password = 0xe5; 8008c2ecf20Sopenharmony_ci break; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci default: 8038c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sorry, no support for %d\n", hardware); 8048c2ecf20Sopenharmony_ci return -ENODEV; 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci return 0; 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic unsigned char snd_miro_read(struct snd_miro *chip, 8118c2ecf20Sopenharmony_ci unsigned char reg) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci unsigned long flags; 8148c2ecf20Sopenharmony_ci unsigned char retval = 0xff; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 8178c2ecf20Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci switch (chip->hardware) { 8208c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C924: 8218c2ecf20Sopenharmony_ci if (reg > 7) { 8228c2ecf20Sopenharmony_ci outb(reg, chip->mc_base + 8); 8238c2ecf20Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 8248c2ecf20Sopenharmony_ci retval = inb(chip->mc_base + 9); 8258c2ecf20Sopenharmony_ci break; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci fallthrough; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C929: 8308c2ecf20Sopenharmony_ci retval = inb(chip->mc_base + reg); 8318c2ecf20Sopenharmony_ci break; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci default: 8348c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware); 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 8388c2ecf20Sopenharmony_ci return retval; 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_cistatic void snd_miro_write(struct snd_miro *chip, unsigned char reg, 8428c2ecf20Sopenharmony_ci unsigned char value) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci unsigned long flags; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 8478c2ecf20Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci switch (chip->hardware) { 8508c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C924: 8518c2ecf20Sopenharmony_ci if (reg > 7) { 8528c2ecf20Sopenharmony_ci outb(reg, chip->mc_base + 8); 8538c2ecf20Sopenharmony_ci outb(chip->password, chip->mc_base + chip->pwd_reg); 8548c2ecf20Sopenharmony_ci outb(value, chip->mc_base + 9); 8558c2ecf20Sopenharmony_ci break; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci fallthrough; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C929: 8608c2ecf20Sopenharmony_ci outb(value, chip->mc_base + reg); 8618c2ecf20Sopenharmony_ci break; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci default: 8648c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware); 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 8688c2ecf20Sopenharmony_ci} 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_cistatic inline void snd_miro_write_mask(struct snd_miro *chip, 8718c2ecf20Sopenharmony_ci unsigned char reg, unsigned char value, unsigned char mask) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci unsigned char oldval = snd_miro_read(chip, reg); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci snd_miro_write(chip, reg, (oldval & ~mask) | (value & mask)); 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci/* 8798c2ecf20Sopenharmony_ci * Proc Interface 8808c2ecf20Sopenharmony_ci */ 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic void snd_miro_proc_read(struct snd_info_entry * entry, 8838c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci struct snd_miro *miro = (struct snd_miro *) entry->private_data; 8868c2ecf20Sopenharmony_ci struct snd_miro_aci *aci = miro->aci; 8878c2ecf20Sopenharmony_ci char* model = "unknown"; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci /* miroSOUND PCM1 pro, early PCM12 */ 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if ((miro->hardware == OPTi9XX_HW_82C929) && 8928c2ecf20Sopenharmony_ci (aci->aci_vendor == 'm') && 8938c2ecf20Sopenharmony_ci (aci->aci_product == 'A')) { 8948c2ecf20Sopenharmony_ci switch (aci->aci_version) { 8958c2ecf20Sopenharmony_ci case 3: 8968c2ecf20Sopenharmony_ci model = "miroSOUND PCM1 pro"; 8978c2ecf20Sopenharmony_ci break; 8988c2ecf20Sopenharmony_ci default: 8998c2ecf20Sopenharmony_ci model = "miroSOUND PCM1 pro / (early) PCM12"; 9008c2ecf20Sopenharmony_ci break; 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci /* miroSOUND PCM12, PCM12 (Rev. E), PCM12 pnp */ 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if ((miro->hardware == OPTi9XX_HW_82C924) && 9078c2ecf20Sopenharmony_ci (aci->aci_vendor == 'm') && 9088c2ecf20Sopenharmony_ci (aci->aci_product == 'B')) { 9098c2ecf20Sopenharmony_ci switch (aci->aci_version) { 9108c2ecf20Sopenharmony_ci case 4: 9118c2ecf20Sopenharmony_ci model = "miroSOUND PCM12"; 9128c2ecf20Sopenharmony_ci break; 9138c2ecf20Sopenharmony_ci case 176: 9148c2ecf20Sopenharmony_ci model = "miroSOUND PCM12 (Rev. E)"; 9158c2ecf20Sopenharmony_ci break; 9168c2ecf20Sopenharmony_ci default: 9178c2ecf20Sopenharmony_ci model = "miroSOUND PCM12 / PCM12 pnp"; 9188c2ecf20Sopenharmony_ci break; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci /* miroSOUND PCM20 radio */ 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if ((miro->hardware == OPTi9XX_HW_82C924) && 9258c2ecf20Sopenharmony_ci (aci->aci_vendor == 'm') && 9268c2ecf20Sopenharmony_ci (aci->aci_product == 'C')) { 9278c2ecf20Sopenharmony_ci switch (aci->aci_version) { 9288c2ecf20Sopenharmony_ci case 7: 9298c2ecf20Sopenharmony_ci model = "miroSOUND PCM20 radio (Rev. E)"; 9308c2ecf20Sopenharmony_ci break; 9318c2ecf20Sopenharmony_ci default: 9328c2ecf20Sopenharmony_ci model = "miroSOUND PCM20 radio"; 9338c2ecf20Sopenharmony_ci break; 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\nGeneral information:\n"); 9388c2ecf20Sopenharmony_ci snd_iprintf(buffer, " model : %s\n", model); 9398c2ecf20Sopenharmony_ci snd_iprintf(buffer, " opti : %s\n", miro->name); 9408c2ecf20Sopenharmony_ci snd_iprintf(buffer, " codec : %s\n", miro->pcm->name); 9418c2ecf20Sopenharmony_ci snd_iprintf(buffer, " port : 0x%lx\n", miro->wss_base); 9428c2ecf20Sopenharmony_ci snd_iprintf(buffer, " irq : %d\n", miro->irq); 9438c2ecf20Sopenharmony_ci snd_iprintf(buffer, " dma : %d,%d\n\n", miro->dma1, miro->dma2); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci snd_iprintf(buffer, "MPU-401:\n"); 9468c2ecf20Sopenharmony_ci snd_iprintf(buffer, " port : 0x%lx\n", miro->mpu_port); 9478c2ecf20Sopenharmony_ci snd_iprintf(buffer, " irq : %d\n\n", miro->mpu_irq); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci snd_iprintf(buffer, "ACI information:\n"); 9508c2ecf20Sopenharmony_ci snd_iprintf(buffer, " vendor : "); 9518c2ecf20Sopenharmony_ci switch (aci->aci_vendor) { 9528c2ecf20Sopenharmony_ci case 'm': 9538c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Miro\n"); 9548c2ecf20Sopenharmony_ci break; 9558c2ecf20Sopenharmony_ci default: 9568c2ecf20Sopenharmony_ci snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_vendor); 9578c2ecf20Sopenharmony_ci break; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci snd_iprintf(buffer, " product : "); 9618c2ecf20Sopenharmony_ci switch (aci->aci_product) { 9628c2ecf20Sopenharmony_ci case 'A': 9638c2ecf20Sopenharmony_ci snd_iprintf(buffer, "miroSOUND PCM1 pro / (early) PCM12\n"); 9648c2ecf20Sopenharmony_ci break; 9658c2ecf20Sopenharmony_ci case 'B': 9668c2ecf20Sopenharmony_ci snd_iprintf(buffer, "miroSOUND PCM12\n"); 9678c2ecf20Sopenharmony_ci break; 9688c2ecf20Sopenharmony_ci case 'C': 9698c2ecf20Sopenharmony_ci snd_iprintf(buffer, "miroSOUND PCM20 radio\n"); 9708c2ecf20Sopenharmony_ci break; 9718c2ecf20Sopenharmony_ci default: 9728c2ecf20Sopenharmony_ci snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_product); 9738c2ecf20Sopenharmony_ci break; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci snd_iprintf(buffer, " firmware: %d (0x%x)\n", 9778c2ecf20Sopenharmony_ci aci->aci_version, aci->aci_version); 9788c2ecf20Sopenharmony_ci snd_iprintf(buffer, " port : 0x%lx-0x%lx\n", 9798c2ecf20Sopenharmony_ci aci->aci_port, aci->aci_port+2); 9808c2ecf20Sopenharmony_ci snd_iprintf(buffer, " wss : 0x%x\n", wss); 9818c2ecf20Sopenharmony_ci snd_iprintf(buffer, " ide : 0x%x\n", ide); 9828c2ecf20Sopenharmony_ci snd_iprintf(buffer, " solomode: 0x%x\n", aci->aci_solomode); 9838c2ecf20Sopenharmony_ci snd_iprintf(buffer, " amp : 0x%x\n", aci->aci_amp); 9848c2ecf20Sopenharmony_ci snd_iprintf(buffer, " preamp : 0x%x\n", aci->aci_preamp); 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_cistatic void snd_miro_proc_init(struct snd_card *card, 9888c2ecf20Sopenharmony_ci struct snd_miro *miro) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci snd_card_ro_proc_new(card, "miro", miro, snd_miro_proc_read); 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci/* 9948c2ecf20Sopenharmony_ci * Init 9958c2ecf20Sopenharmony_ci */ 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic int snd_miro_configure(struct snd_miro *chip) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci unsigned char wss_base_bits; 10008c2ecf20Sopenharmony_ci unsigned char irq_bits; 10018c2ecf20Sopenharmony_ci unsigned char dma_bits; 10028c2ecf20Sopenharmony_ci unsigned char mpu_port_bits = 0; 10038c2ecf20Sopenharmony_ci unsigned char mpu_irq_bits; 10048c2ecf20Sopenharmony_ci unsigned long flags; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80); 10078c2ecf20Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */ 10088c2ecf20Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci switch (chip->hardware) { 10118c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C924: 10128c2ecf20Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02); 10138c2ecf20Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff); 10148c2ecf20Sopenharmony_ci break; 10158c2ecf20Sopenharmony_ci case OPTi9XX_HW_82C929: 10168c2ecf20Sopenharmony_ci /* untested init commands for OPTi929 */ 10178c2ecf20Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c); 10188c2ecf20Sopenharmony_ci break; 10198c2ecf20Sopenharmony_ci default: 10208c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware); 10218c2ecf20Sopenharmony_ci return -EINVAL; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci /* PnP resource says it decodes only 10 bits of address */ 10258c2ecf20Sopenharmony_ci switch (chip->wss_base & 0x3ff) { 10268c2ecf20Sopenharmony_ci case 0x130: 10278c2ecf20Sopenharmony_ci chip->wss_base = 0x530; 10288c2ecf20Sopenharmony_ci wss_base_bits = 0x00; 10298c2ecf20Sopenharmony_ci break; 10308c2ecf20Sopenharmony_ci case 0x204: 10318c2ecf20Sopenharmony_ci chip->wss_base = 0x604; 10328c2ecf20Sopenharmony_ci wss_base_bits = 0x03; 10338c2ecf20Sopenharmony_ci break; 10348c2ecf20Sopenharmony_ci case 0x280: 10358c2ecf20Sopenharmony_ci chip->wss_base = 0xe80; 10368c2ecf20Sopenharmony_ci wss_base_bits = 0x01; 10378c2ecf20Sopenharmony_ci break; 10388c2ecf20Sopenharmony_ci case 0x340: 10398c2ecf20Sopenharmony_ci chip->wss_base = 0xf40; 10408c2ecf20Sopenharmony_ci wss_base_bits = 0x02; 10418c2ecf20Sopenharmony_ci break; 10428c2ecf20Sopenharmony_ci default: 10438c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "WSS port 0x%lx not valid\n", chip->wss_base); 10448c2ecf20Sopenharmony_ci goto __skip_base; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci__skip_base: 10498c2ecf20Sopenharmony_ci switch (chip->irq) { 10508c2ecf20Sopenharmony_ci case 5: 10518c2ecf20Sopenharmony_ci irq_bits = 0x05; 10528c2ecf20Sopenharmony_ci break; 10538c2ecf20Sopenharmony_ci case 7: 10548c2ecf20Sopenharmony_ci irq_bits = 0x01; 10558c2ecf20Sopenharmony_ci break; 10568c2ecf20Sopenharmony_ci case 9: 10578c2ecf20Sopenharmony_ci irq_bits = 0x02; 10588c2ecf20Sopenharmony_ci break; 10598c2ecf20Sopenharmony_ci case 10: 10608c2ecf20Sopenharmony_ci irq_bits = 0x03; 10618c2ecf20Sopenharmony_ci break; 10628c2ecf20Sopenharmony_ci case 11: 10638c2ecf20Sopenharmony_ci irq_bits = 0x04; 10648c2ecf20Sopenharmony_ci break; 10658c2ecf20Sopenharmony_ci default: 10668c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "WSS irq # %d not valid\n", chip->irq); 10678c2ecf20Sopenharmony_ci goto __skip_resources; 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci switch (chip->dma1) { 10718c2ecf20Sopenharmony_ci case 0: 10728c2ecf20Sopenharmony_ci dma_bits = 0x01; 10738c2ecf20Sopenharmony_ci break; 10748c2ecf20Sopenharmony_ci case 1: 10758c2ecf20Sopenharmony_ci dma_bits = 0x02; 10768c2ecf20Sopenharmony_ci break; 10778c2ecf20Sopenharmony_ci case 3: 10788c2ecf20Sopenharmony_ci dma_bits = 0x03; 10798c2ecf20Sopenharmony_ci break; 10808c2ecf20Sopenharmony_ci default: 10818c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "WSS dma1 # %d not valid\n", chip->dma1); 10828c2ecf20Sopenharmony_ci goto __skip_resources; 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci if (chip->dma1 == chip->dma2) { 10868c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "don't want to share dmas\n"); 10878c2ecf20Sopenharmony_ci return -EBUSY; 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci switch (chip->dma2) { 10918c2ecf20Sopenharmony_ci case 0: 10928c2ecf20Sopenharmony_ci case 1: 10938c2ecf20Sopenharmony_ci break; 10948c2ecf20Sopenharmony_ci default: 10958c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "WSS dma2 # %d not valid\n", chip->dma2); 10968c2ecf20Sopenharmony_ci goto __skip_resources; 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci dma_bits |= 0x04; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 11018c2ecf20Sopenharmony_ci outb(irq_bits << 3 | dma_bits, chip->wss_base); 11028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci__skip_resources: 11058c2ecf20Sopenharmony_ci if (chip->hardware > OPTi9XX_HW_82C928) { 11068c2ecf20Sopenharmony_ci switch (chip->mpu_port) { 11078c2ecf20Sopenharmony_ci case 0: 11088c2ecf20Sopenharmony_ci case -1: 11098c2ecf20Sopenharmony_ci break; 11108c2ecf20Sopenharmony_ci case 0x300: 11118c2ecf20Sopenharmony_ci mpu_port_bits = 0x03; 11128c2ecf20Sopenharmony_ci break; 11138c2ecf20Sopenharmony_ci case 0x310: 11148c2ecf20Sopenharmony_ci mpu_port_bits = 0x02; 11158c2ecf20Sopenharmony_ci break; 11168c2ecf20Sopenharmony_ci case 0x320: 11178c2ecf20Sopenharmony_ci mpu_port_bits = 0x01; 11188c2ecf20Sopenharmony_ci break; 11198c2ecf20Sopenharmony_ci case 0x330: 11208c2ecf20Sopenharmony_ci mpu_port_bits = 0x00; 11218c2ecf20Sopenharmony_ci break; 11228c2ecf20Sopenharmony_ci default: 11238c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "MPU-401 port 0x%lx not valid\n", 11248c2ecf20Sopenharmony_ci chip->mpu_port); 11258c2ecf20Sopenharmony_ci goto __skip_mpu; 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci switch (chip->mpu_irq) { 11298c2ecf20Sopenharmony_ci case 5: 11308c2ecf20Sopenharmony_ci mpu_irq_bits = 0x02; 11318c2ecf20Sopenharmony_ci break; 11328c2ecf20Sopenharmony_ci case 7: 11338c2ecf20Sopenharmony_ci mpu_irq_bits = 0x03; 11348c2ecf20Sopenharmony_ci break; 11358c2ecf20Sopenharmony_ci case 9: 11368c2ecf20Sopenharmony_ci mpu_irq_bits = 0x00; 11378c2ecf20Sopenharmony_ci break; 11388c2ecf20Sopenharmony_ci case 10: 11398c2ecf20Sopenharmony_ci mpu_irq_bits = 0x01; 11408c2ecf20Sopenharmony_ci break; 11418c2ecf20Sopenharmony_ci default: 11428c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "MPU-401 irq # %d not valid\n", 11438c2ecf20Sopenharmony_ci chip->mpu_irq); 11448c2ecf20Sopenharmony_ci goto __skip_mpu; 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci snd_miro_write_mask(chip, OPTi9XX_MC_REG(6), 11488c2ecf20Sopenharmony_ci (chip->mpu_port <= 0) ? 0x00 : 11498c2ecf20Sopenharmony_ci 0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3, 11508c2ecf20Sopenharmony_ci 0xf8); 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci__skip_mpu: 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci return 0; 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_cistatic int snd_miro_opti_check(struct snd_miro *chip) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci unsigned char value; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, 11628c2ecf20Sopenharmony_ci "OPTi9xx MC"); 11638c2ecf20Sopenharmony_ci if (chip->res_mc_base == NULL) 11648c2ecf20Sopenharmony_ci return -ENOMEM; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci value = snd_miro_read(chip, OPTi9XX_MC_REG(1)); 11678c2ecf20Sopenharmony_ci if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1))) 11688c2ecf20Sopenharmony_ci if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1))) 11698c2ecf20Sopenharmony_ci return 0; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci release_and_free_resource(chip->res_mc_base); 11728c2ecf20Sopenharmony_ci chip->res_mc_base = NULL; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci return -ENODEV; 11758c2ecf20Sopenharmony_ci} 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_cistatic int snd_card_miro_detect(struct snd_card *card, 11788c2ecf20Sopenharmony_ci struct snd_miro *chip) 11798c2ecf20Sopenharmony_ci{ 11808c2ecf20Sopenharmony_ci int i, err; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci for (i = OPTi9XX_HW_82C929; i <= OPTi9XX_HW_82C924; i++) { 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci if ((err = snd_miro_init(chip, i)) < 0) 11858c2ecf20Sopenharmony_ci return err; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci err = snd_miro_opti_check(chip); 11888c2ecf20Sopenharmony_ci if (err == 0) 11898c2ecf20Sopenharmony_ci return 1; 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci return -ENODEV; 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic int snd_card_miro_aci_detect(struct snd_card *card, 11968c2ecf20Sopenharmony_ci struct snd_miro *miro) 11978c2ecf20Sopenharmony_ci{ 11988c2ecf20Sopenharmony_ci unsigned char regval; 11998c2ecf20Sopenharmony_ci int i; 12008c2ecf20Sopenharmony_ci struct snd_miro_aci *aci = &aci_device; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci miro->aci = aci; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci mutex_init(&aci->aci_mutex); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci /* get ACI port from OPTi9xx MC 4 */ 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci regval=inb(miro->mc_base + 4); 12098c2ecf20Sopenharmony_ci aci->aci_port = (regval & 0x10) ? 0x344 : 0x354; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci miro->res_aci_port = request_region(aci->aci_port, 3, "miro aci"); 12128c2ecf20Sopenharmony_ci if (miro->res_aci_port == NULL) { 12138c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "aci i/o area 0x%lx-0x%lx already used.\n", 12148c2ecf20Sopenharmony_ci aci->aci_port, aci->aci_port+2); 12158c2ecf20Sopenharmony_ci return -ENOMEM; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci /* force ACI into a known state */ 12198c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) 12208c2ecf20Sopenharmony_ci if (snd_aci_cmd(aci, ACI_ERROR_OP, -1, -1) < 0) { 12218c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "can't force aci into known state.\n"); 12228c2ecf20Sopenharmony_ci return -ENXIO; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci aci->aci_vendor = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1); 12268c2ecf20Sopenharmony_ci aci->aci_product = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1); 12278c2ecf20Sopenharmony_ci if (aci->aci_vendor < 0 || aci->aci_product < 0) { 12288c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n", 12298c2ecf20Sopenharmony_ci aci->aci_port); 12308c2ecf20Sopenharmony_ci return -ENXIO; 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci aci->aci_version = snd_aci_cmd(aci, ACI_READ_VERSION, -1, -1); 12348c2ecf20Sopenharmony_ci if (aci->aci_version < 0) { 12358c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "can't read aci version on 0x%lx.\n", 12368c2ecf20Sopenharmony_ci aci->aci_port); 12378c2ecf20Sopenharmony_ci return -ENXIO; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci if (snd_aci_cmd(aci, ACI_INIT, -1, -1) < 0 || 12418c2ecf20Sopenharmony_ci snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 || 12428c2ecf20Sopenharmony_ci snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) { 12438c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "can't initialize aci.\n"); 12448c2ecf20Sopenharmony_ci return -ENXIO; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci return 0; 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic void snd_card_miro_free(struct snd_card *card) 12518c2ecf20Sopenharmony_ci{ 12528c2ecf20Sopenharmony_ci struct snd_miro *miro = card->private_data; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci release_and_free_resource(miro->res_aci_port); 12558c2ecf20Sopenharmony_ci if (miro->aci) 12568c2ecf20Sopenharmony_ci miro->aci->aci_port = 0; 12578c2ecf20Sopenharmony_ci release_and_free_resource(miro->res_mc_base); 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_cistatic int snd_miro_probe(struct snd_card *card) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci int error; 12638c2ecf20Sopenharmony_ci struct snd_miro *miro = card->private_data; 12648c2ecf20Sopenharmony_ci struct snd_wss *codec; 12658c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci if (!miro->res_mc_base) { 12688c2ecf20Sopenharmony_ci miro->res_mc_base = request_region(miro->mc_base, 12698c2ecf20Sopenharmony_ci miro->mc_base_size, 12708c2ecf20Sopenharmony_ci "miro (OPTi9xx MC)"); 12718c2ecf20Sopenharmony_ci if (miro->res_mc_base == NULL) { 12728c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "request for OPTI9xx MC failed\n"); 12738c2ecf20Sopenharmony_ci return -ENOMEM; 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci error = snd_card_miro_aci_detect(card, miro); 12788c2ecf20Sopenharmony_ci if (error < 0) { 12798c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "unable to detect aci chip\n"); 12808c2ecf20Sopenharmony_ci return -ENODEV; 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci miro->wss_base = port; 12848c2ecf20Sopenharmony_ci miro->mpu_port = mpu_port; 12858c2ecf20Sopenharmony_ci miro->irq = irq; 12868c2ecf20Sopenharmony_ci miro->mpu_irq = mpu_irq; 12878c2ecf20Sopenharmony_ci miro->dma1 = dma1; 12888c2ecf20Sopenharmony_ci miro->dma2 = dma2; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci /* init proc interface */ 12918c2ecf20Sopenharmony_ci snd_miro_proc_init(card, miro); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci error = snd_miro_configure(miro); 12948c2ecf20Sopenharmony_ci if (error) 12958c2ecf20Sopenharmony_ci return error; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci error = snd_wss_create(card, miro->wss_base + 4, -1, 12988c2ecf20Sopenharmony_ci miro->irq, miro->dma1, miro->dma2, 12998c2ecf20Sopenharmony_ci WSS_HW_DETECT, 0, &codec); 13008c2ecf20Sopenharmony_ci if (error < 0) 13018c2ecf20Sopenharmony_ci return error; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci error = snd_wss_pcm(codec, 0); 13048c2ecf20Sopenharmony_ci if (error < 0) 13058c2ecf20Sopenharmony_ci return error; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci error = snd_wss_mixer(codec); 13088c2ecf20Sopenharmony_ci if (error < 0) 13098c2ecf20Sopenharmony_ci return error; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci error = snd_wss_timer(codec, 0); 13128c2ecf20Sopenharmony_ci if (error < 0) 13138c2ecf20Sopenharmony_ci return error; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci miro->pcm = codec->pcm; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci error = snd_miro_mixer(card, miro); 13188c2ecf20Sopenharmony_ci if (error < 0) 13198c2ecf20Sopenharmony_ci return error; 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci if (miro->aci->aci_vendor == 'm') { 13228c2ecf20Sopenharmony_ci /* It looks like a miro sound card. */ 13238c2ecf20Sopenharmony_ci switch (miro->aci->aci_product) { 13248c2ecf20Sopenharmony_ci case 'A': 13258c2ecf20Sopenharmony_ci sprintf(card->shortname, 13268c2ecf20Sopenharmony_ci "miroSOUND PCM1 pro / PCM12"); 13278c2ecf20Sopenharmony_ci break; 13288c2ecf20Sopenharmony_ci case 'B': 13298c2ecf20Sopenharmony_ci sprintf(card->shortname, 13308c2ecf20Sopenharmony_ci "miroSOUND PCM12"); 13318c2ecf20Sopenharmony_ci break; 13328c2ecf20Sopenharmony_ci case 'C': 13338c2ecf20Sopenharmony_ci sprintf(card->shortname, 13348c2ecf20Sopenharmony_ci "miroSOUND PCM20 radio"); 13358c2ecf20Sopenharmony_ci break; 13368c2ecf20Sopenharmony_ci default: 13378c2ecf20Sopenharmony_ci sprintf(card->shortname, 13388c2ecf20Sopenharmony_ci "unknown miro"); 13398c2ecf20Sopenharmony_ci snd_printk(KERN_INFO "unknown miro aci id\n"); 13408c2ecf20Sopenharmony_ci break; 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci } else { 13438c2ecf20Sopenharmony_ci snd_printk(KERN_INFO "found unsupported aci card\n"); 13448c2ecf20Sopenharmony_ci sprintf(card->shortname, "unknown Cardinal Technologies"); 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci strcpy(card->driver, "miro"); 13488c2ecf20Sopenharmony_ci snprintf(card->longname, sizeof(card->longname), 13498c2ecf20Sopenharmony_ci "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d", 13508c2ecf20Sopenharmony_ci card->shortname, miro->name, codec->pcm->name, 13518c2ecf20Sopenharmony_ci miro->wss_base + 4, miro->irq, miro->dma1, miro->dma2); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT) 13548c2ecf20Sopenharmony_ci rmidi = NULL; 13558c2ecf20Sopenharmony_ci else { 13568c2ecf20Sopenharmony_ci error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, 13578c2ecf20Sopenharmony_ci mpu_port, 0, miro->mpu_irq, &rmidi); 13588c2ecf20Sopenharmony_ci if (error < 0) 13598c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", 13608c2ecf20Sopenharmony_ci mpu_port); 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) { 13648c2ecf20Sopenharmony_ci struct snd_opl3 *opl3 = NULL; 13658c2ecf20Sopenharmony_ci struct snd_opl4 *opl4; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci if (snd_opl4_create(card, fm_port, fm_port - 8, 13688c2ecf20Sopenharmony_ci 2, &opl3, &opl4) < 0) 13698c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n", 13708c2ecf20Sopenharmony_ci fm_port); 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci error = snd_set_aci_init_values(miro); 13748c2ecf20Sopenharmony_ci if (error < 0) 13758c2ecf20Sopenharmony_ci return error; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci return snd_card_register(card); 13788c2ecf20Sopenharmony_ci} 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_cistatic int snd_miro_isa_match(struct device *devptr, unsigned int n) 13818c2ecf20Sopenharmony_ci{ 13828c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 13838c2ecf20Sopenharmony_ci if (snd_miro_pnp_is_probed) 13848c2ecf20Sopenharmony_ci return 0; 13858c2ecf20Sopenharmony_ci if (isapnp) 13868c2ecf20Sopenharmony_ci return 0; 13878c2ecf20Sopenharmony_ci#endif 13888c2ecf20Sopenharmony_ci return 1; 13898c2ecf20Sopenharmony_ci} 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_cistatic int snd_miro_isa_probe(struct device *devptr, unsigned int n) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci static const long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1}; 13948c2ecf20Sopenharmony_ci static const long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1}; 13958c2ecf20Sopenharmony_ci static const int possible_irqs[] = {11, 9, 10, 7, -1}; 13968c2ecf20Sopenharmony_ci static const int possible_mpu_irqs[] = {10, 5, 9, 7, -1}; 13978c2ecf20Sopenharmony_ci static const int possible_dma1s[] = {3, 1, 0, -1}; 13988c2ecf20Sopenharmony_ci static const int possible_dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1}, 13998c2ecf20Sopenharmony_ci {0, -1} }; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci int error; 14028c2ecf20Sopenharmony_ci struct snd_miro *miro; 14038c2ecf20Sopenharmony_ci struct snd_card *card; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci error = snd_card_new(devptr, index, id, THIS_MODULE, 14068c2ecf20Sopenharmony_ci sizeof(struct snd_miro), &card); 14078c2ecf20Sopenharmony_ci if (error < 0) 14088c2ecf20Sopenharmony_ci return error; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci card->private_free = snd_card_miro_free; 14118c2ecf20Sopenharmony_ci miro = card->private_data; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci error = snd_card_miro_detect(card, miro); 14148c2ecf20Sopenharmony_ci if (error < 0) { 14158c2ecf20Sopenharmony_ci snd_card_free(card); 14168c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n"); 14178c2ecf20Sopenharmony_ci return -ENODEV; 14188c2ecf20Sopenharmony_ci } 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci if (port == SNDRV_AUTO_PORT) { 14218c2ecf20Sopenharmony_ci port = snd_legacy_find_free_ioport(possible_ports, 4); 14228c2ecf20Sopenharmony_ci if (port < 0) { 14238c2ecf20Sopenharmony_ci snd_card_free(card); 14248c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free WSS port\n"); 14258c2ecf20Sopenharmony_ci return -EBUSY; 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci if (mpu_port == SNDRV_AUTO_PORT) { 14308c2ecf20Sopenharmony_ci mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2); 14318c2ecf20Sopenharmony_ci if (mpu_port < 0) { 14328c2ecf20Sopenharmony_ci snd_card_free(card); 14338c2ecf20Sopenharmony_ci snd_printk(KERN_ERR 14348c2ecf20Sopenharmony_ci "unable to find a free MPU401 port\n"); 14358c2ecf20Sopenharmony_ci return -EBUSY; 14368c2ecf20Sopenharmony_ci } 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci if (irq == SNDRV_AUTO_IRQ) { 14408c2ecf20Sopenharmony_ci irq = snd_legacy_find_free_irq(possible_irqs); 14418c2ecf20Sopenharmony_ci if (irq < 0) { 14428c2ecf20Sopenharmony_ci snd_card_free(card); 14438c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free IRQ\n"); 14448c2ecf20Sopenharmony_ci return -EBUSY; 14458c2ecf20Sopenharmony_ci } 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci if (mpu_irq == SNDRV_AUTO_IRQ) { 14488c2ecf20Sopenharmony_ci mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs); 14498c2ecf20Sopenharmony_ci if (mpu_irq < 0) { 14508c2ecf20Sopenharmony_ci snd_card_free(card); 14518c2ecf20Sopenharmony_ci snd_printk(KERN_ERR 14528c2ecf20Sopenharmony_ci "unable to find a free MPU401 IRQ\n"); 14538c2ecf20Sopenharmony_ci return -EBUSY; 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci } 14568c2ecf20Sopenharmony_ci if (dma1 == SNDRV_AUTO_DMA) { 14578c2ecf20Sopenharmony_ci dma1 = snd_legacy_find_free_dma(possible_dma1s); 14588c2ecf20Sopenharmony_ci if (dma1 < 0) { 14598c2ecf20Sopenharmony_ci snd_card_free(card); 14608c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free DMA1\n"); 14618c2ecf20Sopenharmony_ci return -EBUSY; 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci } 14648c2ecf20Sopenharmony_ci if (dma2 == SNDRV_AUTO_DMA) { 14658c2ecf20Sopenharmony_ci dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]); 14668c2ecf20Sopenharmony_ci if (dma2 < 0) { 14678c2ecf20Sopenharmony_ci snd_card_free(card); 14688c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "unable to find a free DMA2\n"); 14698c2ecf20Sopenharmony_ci return -EBUSY; 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci error = snd_miro_probe(card); 14748c2ecf20Sopenharmony_ci if (error < 0) { 14758c2ecf20Sopenharmony_ci snd_card_free(card); 14768c2ecf20Sopenharmony_ci return error; 14778c2ecf20Sopenharmony_ci } 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci dev_set_drvdata(devptr, card); 14808c2ecf20Sopenharmony_ci return 0; 14818c2ecf20Sopenharmony_ci} 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_cistatic int snd_miro_isa_remove(struct device *devptr, 14848c2ecf20Sopenharmony_ci unsigned int dev) 14858c2ecf20Sopenharmony_ci{ 14868c2ecf20Sopenharmony_ci snd_card_free(dev_get_drvdata(devptr)); 14878c2ecf20Sopenharmony_ci return 0; 14888c2ecf20Sopenharmony_ci} 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci#define DEV_NAME "miro" 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_cistatic struct isa_driver snd_miro_driver = { 14938c2ecf20Sopenharmony_ci .match = snd_miro_isa_match, 14948c2ecf20Sopenharmony_ci .probe = snd_miro_isa_probe, 14958c2ecf20Sopenharmony_ci .remove = snd_miro_isa_remove, 14968c2ecf20Sopenharmony_ci /* FIXME: suspend/resume */ 14978c2ecf20Sopenharmony_ci .driver = { 14988c2ecf20Sopenharmony_ci .name = DEV_NAME 14998c2ecf20Sopenharmony_ci }, 15008c2ecf20Sopenharmony_ci}; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_cistatic int snd_card_miro_pnp(struct snd_miro *chip, 15058c2ecf20Sopenharmony_ci struct pnp_card_link *card, 15068c2ecf20Sopenharmony_ci const struct pnp_card_device_id *pid) 15078c2ecf20Sopenharmony_ci{ 15088c2ecf20Sopenharmony_ci struct pnp_dev *pdev; 15098c2ecf20Sopenharmony_ci int err; 15108c2ecf20Sopenharmony_ci struct pnp_dev *devmpu; 15118c2ecf20Sopenharmony_ci struct pnp_dev *devmc; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci pdev = pnp_request_card_device(card, pid->devs[0].id, NULL); 15148c2ecf20Sopenharmony_ci if (pdev == NULL) 15158c2ecf20Sopenharmony_ci return -EBUSY; 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL); 15188c2ecf20Sopenharmony_ci if (devmpu == NULL) 15198c2ecf20Sopenharmony_ci return -EBUSY; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci devmc = pnp_request_card_device(card, pid->devs[2].id, NULL); 15228c2ecf20Sopenharmony_ci if (devmc == NULL) 15238c2ecf20Sopenharmony_ci return -EBUSY; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci err = pnp_activate_dev(pdev); 15268c2ecf20Sopenharmony_ci if (err < 0) { 15278c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err); 15288c2ecf20Sopenharmony_ci return err; 15298c2ecf20Sopenharmony_ci } 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci err = pnp_activate_dev(devmc); 15328c2ecf20Sopenharmony_ci if (err < 0) { 15338c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "MC pnp configure failure: %d\n", 15348c2ecf20Sopenharmony_ci err); 15358c2ecf20Sopenharmony_ci return err; 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci port = pnp_port_start(pdev, 1); 15398c2ecf20Sopenharmony_ci fm_port = pnp_port_start(pdev, 2) + 8; 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci /* 15428c2ecf20Sopenharmony_ci * The MC(0) is never accessed and the miroSOUND PCM20 card does not 15438c2ecf20Sopenharmony_ci * include it in the PnP resource range. OPTI93x include it. 15448c2ecf20Sopenharmony_ci */ 15458c2ecf20Sopenharmony_ci chip->mc_base = pnp_port_start(devmc, 0) - 1; 15468c2ecf20Sopenharmony_ci chip->mc_base_size = pnp_port_len(devmc, 0) + 1; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci irq = pnp_irq(pdev, 0); 15498c2ecf20Sopenharmony_ci dma1 = pnp_dma(pdev, 0); 15508c2ecf20Sopenharmony_ci dma2 = pnp_dma(pdev, 1); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci if (mpu_port > 0) { 15538c2ecf20Sopenharmony_ci err = pnp_activate_dev(devmpu); 15548c2ecf20Sopenharmony_ci if (err < 0) { 15558c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "MPU401 pnp configure failure\n"); 15568c2ecf20Sopenharmony_ci mpu_port = -1; 15578c2ecf20Sopenharmony_ci return err; 15588c2ecf20Sopenharmony_ci } 15598c2ecf20Sopenharmony_ci mpu_port = pnp_port_start(devmpu, 0); 15608c2ecf20Sopenharmony_ci mpu_irq = pnp_irq(devmpu, 0); 15618c2ecf20Sopenharmony_ci } 15628c2ecf20Sopenharmony_ci return 0; 15638c2ecf20Sopenharmony_ci} 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_cistatic int snd_miro_pnp_probe(struct pnp_card_link *pcard, 15668c2ecf20Sopenharmony_ci const struct pnp_card_device_id *pid) 15678c2ecf20Sopenharmony_ci{ 15688c2ecf20Sopenharmony_ci struct snd_card *card; 15698c2ecf20Sopenharmony_ci int err; 15708c2ecf20Sopenharmony_ci struct snd_miro *miro; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci if (snd_miro_pnp_is_probed) 15738c2ecf20Sopenharmony_ci return -EBUSY; 15748c2ecf20Sopenharmony_ci if (!isapnp) 15758c2ecf20Sopenharmony_ci return -ENODEV; 15768c2ecf20Sopenharmony_ci err = snd_card_new(&pcard->card->dev, index, id, THIS_MODULE, 15778c2ecf20Sopenharmony_ci sizeof(struct snd_miro), &card); 15788c2ecf20Sopenharmony_ci if (err < 0) 15798c2ecf20Sopenharmony_ci return err; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci card->private_free = snd_card_miro_free; 15828c2ecf20Sopenharmony_ci miro = card->private_data; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci err = snd_card_miro_pnp(miro, pcard, pid); 15858c2ecf20Sopenharmony_ci if (err) { 15868c2ecf20Sopenharmony_ci snd_card_free(card); 15878c2ecf20Sopenharmony_ci return err; 15888c2ecf20Sopenharmony_ci } 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci /* only miroSOUND PCM20 and PCM12 == OPTi924 */ 15918c2ecf20Sopenharmony_ci err = snd_miro_init(miro, OPTi9XX_HW_82C924); 15928c2ecf20Sopenharmony_ci if (err) { 15938c2ecf20Sopenharmony_ci snd_card_free(card); 15948c2ecf20Sopenharmony_ci return err; 15958c2ecf20Sopenharmony_ci } 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci err = snd_miro_opti_check(miro); 15988c2ecf20Sopenharmony_ci if (err) { 15998c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "OPTI chip not found\n"); 16008c2ecf20Sopenharmony_ci snd_card_free(card); 16018c2ecf20Sopenharmony_ci return err; 16028c2ecf20Sopenharmony_ci } 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci err = snd_miro_probe(card); 16058c2ecf20Sopenharmony_ci if (err < 0) { 16068c2ecf20Sopenharmony_ci snd_card_free(card); 16078c2ecf20Sopenharmony_ci return err; 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci pnp_set_card_drvdata(pcard, card); 16108c2ecf20Sopenharmony_ci snd_miro_pnp_is_probed = 1; 16118c2ecf20Sopenharmony_ci return 0; 16128c2ecf20Sopenharmony_ci} 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_cistatic void snd_miro_pnp_remove(struct pnp_card_link *pcard) 16158c2ecf20Sopenharmony_ci{ 16168c2ecf20Sopenharmony_ci snd_card_free(pnp_get_card_drvdata(pcard)); 16178c2ecf20Sopenharmony_ci pnp_set_card_drvdata(pcard, NULL); 16188c2ecf20Sopenharmony_ci snd_miro_pnp_is_probed = 0; 16198c2ecf20Sopenharmony_ci} 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_cistatic struct pnp_card_driver miro_pnpc_driver = { 16228c2ecf20Sopenharmony_ci .flags = PNP_DRIVER_RES_DISABLE, 16238c2ecf20Sopenharmony_ci .name = "miro", 16248c2ecf20Sopenharmony_ci .id_table = snd_miro_pnpids, 16258c2ecf20Sopenharmony_ci .probe = snd_miro_pnp_probe, 16268c2ecf20Sopenharmony_ci .remove = snd_miro_pnp_remove, 16278c2ecf20Sopenharmony_ci}; 16288c2ecf20Sopenharmony_ci#endif 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_cistatic int __init alsa_card_miro_init(void) 16318c2ecf20Sopenharmony_ci{ 16328c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 16338c2ecf20Sopenharmony_ci pnp_register_card_driver(&miro_pnpc_driver); 16348c2ecf20Sopenharmony_ci if (snd_miro_pnp_is_probed) 16358c2ecf20Sopenharmony_ci return 0; 16368c2ecf20Sopenharmony_ci pnp_unregister_card_driver(&miro_pnpc_driver); 16378c2ecf20Sopenharmony_ci#endif 16388c2ecf20Sopenharmony_ci return isa_register_driver(&snd_miro_driver, 1); 16398c2ecf20Sopenharmony_ci} 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_cistatic void __exit alsa_card_miro_exit(void) 16428c2ecf20Sopenharmony_ci{ 16438c2ecf20Sopenharmony_ci if (!snd_miro_pnp_is_probed) { 16448c2ecf20Sopenharmony_ci isa_unregister_driver(&snd_miro_driver); 16458c2ecf20Sopenharmony_ci return; 16468c2ecf20Sopenharmony_ci } 16478c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP 16488c2ecf20Sopenharmony_ci pnp_unregister_card_driver(&miro_pnpc_driver); 16498c2ecf20Sopenharmony_ci#endif 16508c2ecf20Sopenharmony_ci} 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_cimodule_init(alsa_card_miro_init) 16538c2ecf20Sopenharmony_cimodule_exit(alsa_card_miro_exit) 1654