18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Gallant SC-6000 soundcard. This card is also known as 48c2ecf20Sopenharmony_ci * Audio Excel DSP 16 or Zoltrix AV302. 58c2ecf20Sopenharmony_ci * These cards use CompuMedia ASC-9308 chip + AD1848 codec. 68c2ecf20Sopenharmony_ci * SC-6600 and SC-7000 cards are also supported. They are based on 78c2ecf20Sopenharmony_ci * CompuMedia ASC-9408 chip and CS4231 codec. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * I don't have documentation for this card. I used the driver 128c2ecf20Sopenharmony_ci * for OSS/Free included in the kernel source as reference. 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/isa.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <asm/dma.h> 208c2ecf20Sopenharmony_ci#include <sound/core.h> 218c2ecf20Sopenharmony_ci#include <sound/wss.h> 228c2ecf20Sopenharmony_ci#include <sound/opl3.h> 238c2ecf20Sopenharmony_ci#include <sound/mpu401.h> 248c2ecf20Sopenharmony_ci#include <sound/control.h> 258c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IRQ 268c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_DMA 278c2ecf20Sopenharmony_ci#include <sound/initval.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Krzysztof Helt"); 308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Gallant SC-6000"); 318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 328c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Gallant, SC-6000}," 338c2ecf20Sopenharmony_ci "{AudioExcel, Audio Excel DSP 16}," 348c2ecf20Sopenharmony_ci "{Zoltrix, AV302}}"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 378c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 388c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ 398c2ecf20Sopenharmony_cistatic long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220, 0x240 */ 408c2ecf20Sopenharmony_cistatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5, 7, 9, 10, 11 */ 418c2ecf20Sopenharmony_cistatic long mss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530, 0xe80 */ 428c2ecf20Sopenharmony_cistatic long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 438c2ecf20Sopenharmony_ci /* 0x300, 0x310, 0x320, 0x330 */ 448c2ecf20Sopenharmony_cistatic int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5, 7, 9, 10, 0 */ 458c2ecf20Sopenharmony_cistatic int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0, 1, 3 */ 468c2ecf20Sopenharmony_cistatic bool joystick[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = false }; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for sc-6000 based soundcard."); 508c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for sc-6000 based soundcard."); 528c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable sc-6000 based soundcard."); 548c2ecf20Sopenharmony_cimodule_param_hw_array(port, long, ioport, NULL, 0444); 558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(port, "Port # for sc-6000 driver."); 568c2ecf20Sopenharmony_cimodule_param_hw_array(mss_port, long, ioport, NULL, 0444); 578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mss_port, "MSS Port # for sc-6000 driver."); 588c2ecf20Sopenharmony_cimodule_param_hw_array(mpu_port, long, ioport, NULL, 0444); 598c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mpu_port, "MPU-401 port # for sc-6000 driver."); 608c2ecf20Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0444); 618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ # for sc-6000 driver."); 628c2ecf20Sopenharmony_cimodule_param_hw_array(mpu_irq, int, irq, NULL, 0444); 638c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver."); 648c2ecf20Sopenharmony_cimodule_param_hw_array(dma, int, dma, NULL, 0444); 658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma, "DMA # for sc-6000 driver."); 668c2ecf20Sopenharmony_cimodule_param_array(joystick, bool, NULL, 0444); 678c2ecf20Sopenharmony_ciMODULE_PARM_DESC(joystick, "Enable gameport."); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * Commands of SC6000's DSP (SBPRO+special). 718c2ecf20Sopenharmony_ci * Some of them are COMMAND_xx, in the future they may change. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci#define WRITE_MDIRQ_CFG 0x50 /* Set M&I&DRQ mask (the real config) */ 748c2ecf20Sopenharmony_ci#define COMMAND_52 0x52 /* */ 758c2ecf20Sopenharmony_ci#define READ_HARD_CFG 0x58 /* Read Hardware Config (I/O base etc) */ 768c2ecf20Sopenharmony_ci#define COMMAND_5C 0x5c /* */ 778c2ecf20Sopenharmony_ci#define COMMAND_60 0x60 /* */ 788c2ecf20Sopenharmony_ci#define COMMAND_66 0x66 /* */ 798c2ecf20Sopenharmony_ci#define COMMAND_6C 0x6c /* */ 808c2ecf20Sopenharmony_ci#define COMMAND_6E 0x6e /* */ 818c2ecf20Sopenharmony_ci#define COMMAND_88 0x88 /* Unknown command */ 828c2ecf20Sopenharmony_ci#define DSP_INIT_MSS 0x8c /* Enable Microsoft Sound System mode */ 838c2ecf20Sopenharmony_ci#define COMMAND_C5 0xc5 /* */ 848c2ecf20Sopenharmony_ci#define GET_DSP_VERSION 0xe1 /* Get DSP Version */ 858c2ecf20Sopenharmony_ci#define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* 888c2ecf20Sopenharmony_ci * Offsets of SC6000 DSP I/O ports. The offset is added to base I/O port 898c2ecf20Sopenharmony_ci * to have the actual I/O port. 908c2ecf20Sopenharmony_ci * Register permissions are: 918c2ecf20Sopenharmony_ci * (wo) == Write Only 928c2ecf20Sopenharmony_ci * (ro) == Read Only 938c2ecf20Sopenharmony_ci * (w-) == Write 948c2ecf20Sopenharmony_ci * (r-) == Read 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci#define DSP_RESET 0x06 /* offset of DSP RESET (wo) */ 978c2ecf20Sopenharmony_ci#define DSP_READ 0x0a /* offset of DSP READ (ro) */ 988c2ecf20Sopenharmony_ci#define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */ 998c2ecf20Sopenharmony_ci#define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */ 1008c2ecf20Sopenharmony_ci#define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */ 1018c2ecf20Sopenharmony_ci#define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */ 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define PFX "sc6000: " 1048c2ecf20Sopenharmony_ci#define DRV_NAME "SC-6000" 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* hardware dependent functions */ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* 1098c2ecf20Sopenharmony_ci * sc6000_irq_to_softcfg - Decode irq number into cfg code. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_cistatic unsigned char sc6000_irq_to_softcfg(int irq) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci unsigned char val = 0; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci switch (irq) { 1168c2ecf20Sopenharmony_ci case 5: 1178c2ecf20Sopenharmony_ci val = 0x28; 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci case 7: 1208c2ecf20Sopenharmony_ci val = 0x8; 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci case 9: 1238c2ecf20Sopenharmony_ci val = 0x10; 1248c2ecf20Sopenharmony_ci break; 1258c2ecf20Sopenharmony_ci case 10: 1268c2ecf20Sopenharmony_ci val = 0x18; 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci case 11: 1298c2ecf20Sopenharmony_ci val = 0x20; 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci default: 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci return val; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* 1388c2ecf20Sopenharmony_ci * sc6000_dma_to_softcfg - Decode dma number into cfg code. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_cistatic unsigned char sc6000_dma_to_softcfg(int dma) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci unsigned char val = 0; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci switch (dma) { 1458c2ecf20Sopenharmony_ci case 0: 1468c2ecf20Sopenharmony_ci val = 1; 1478c2ecf20Sopenharmony_ci break; 1488c2ecf20Sopenharmony_ci case 1: 1498c2ecf20Sopenharmony_ci val = 2; 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci case 3: 1528c2ecf20Sopenharmony_ci val = 3; 1538c2ecf20Sopenharmony_ci break; 1548c2ecf20Sopenharmony_ci default: 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci return val; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* 1618c2ecf20Sopenharmony_ci * sc6000_mpu_irq_to_softcfg - Decode MPU-401 irq number into cfg code. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_cistatic unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci unsigned char val = 0; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci switch (mpu_irq) { 1688c2ecf20Sopenharmony_ci case 5: 1698c2ecf20Sopenharmony_ci val = 4; 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci case 7: 1728c2ecf20Sopenharmony_ci val = 0x44; 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci case 9: 1758c2ecf20Sopenharmony_ci val = 0x84; 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci case 10: 1788c2ecf20Sopenharmony_ci val = 0xc4; 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci default: 1818c2ecf20Sopenharmony_ci break; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci return val; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int sc6000_wait_data(char __iomem *vport) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci int loop = 1000; 1898c2ecf20Sopenharmony_ci unsigned char val = 0; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci do { 1928c2ecf20Sopenharmony_ci val = ioread8(vport + DSP_DATAVAIL); 1938c2ecf20Sopenharmony_ci if (val & 0x80) 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci cpu_relax(); 1968c2ecf20Sopenharmony_ci } while (loop--); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return -EAGAIN; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int sc6000_read(char __iomem *vport) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci if (sc6000_wait_data(vport)) 2048c2ecf20Sopenharmony_ci return -EBUSY; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return ioread8(vport + DSP_READ); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int sc6000_write(char __iomem *vport, int cmd) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci unsigned char val; 2138c2ecf20Sopenharmony_ci int loop = 500000; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci do { 2168c2ecf20Sopenharmony_ci val = ioread8(vport + DSP_STATUS); 2178c2ecf20Sopenharmony_ci /* 2188c2ecf20Sopenharmony_ci * DSP ready to receive data if bit 7 of val == 0 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci if (!(val & 0x80)) { 2218c2ecf20Sopenharmony_ci iowrite8(cmd, vport + DSP_COMMAND); 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci cpu_relax(); 2258c2ecf20Sopenharmony_ci } while (loop--); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "DSP Command (0x%x) timeout.\n", cmd); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return -EIO; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic int sc6000_dsp_get_answer(char __iomem *vport, int command, 2338c2ecf20Sopenharmony_ci char *data, int data_len) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci int len = 0; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (sc6000_write(vport, command)) { 2388c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "CMD 0x%x: failed!\n", command); 2398c2ecf20Sopenharmony_ci return -EIO; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci do { 2438c2ecf20Sopenharmony_ci int val = sc6000_read(vport); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (val < 0) 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci data[len++] = val; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci } while (len < data_len); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* 2538c2ecf20Sopenharmony_ci * If no more data available, return to the caller, no error if len>0. 2548c2ecf20Sopenharmony_ci * We have no other way to know when the string is finished. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ci return len ? len : -EIO; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int sc6000_dsp_reset(char __iomem *vport) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci iowrite8(1, vport + DSP_RESET); 2628c2ecf20Sopenharmony_ci udelay(10); 2638c2ecf20Sopenharmony_ci iowrite8(0, vport + DSP_RESET); 2648c2ecf20Sopenharmony_ci udelay(20); 2658c2ecf20Sopenharmony_ci if (sc6000_read(vport) == 0xaa) 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci return -ENODEV; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* detection and initialization */ 2718c2ecf20Sopenharmony_cistatic int sc6000_hw_cfg_write(char __iomem *vport, const int *cfg) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci if (sc6000_write(vport, COMMAND_6C) < 0) { 2748c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "CMD 0x%x: failed!\n", COMMAND_6C); 2758c2ecf20Sopenharmony_ci return -EIO; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci if (sc6000_write(vport, COMMAND_5C) < 0) { 2788c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_5C); 2798c2ecf20Sopenharmony_ci return -EIO; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci if (sc6000_write(vport, cfg[0]) < 0) { 2828c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[0]); 2838c2ecf20Sopenharmony_ci return -EIO; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci if (sc6000_write(vport, cfg[1]) < 0) { 2868c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[1]); 2878c2ecf20Sopenharmony_ci return -EIO; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci if (sc6000_write(vport, COMMAND_C5) < 0) { 2908c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_C5); 2918c2ecf20Sopenharmony_ci return -EIO; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int sc6000_cfg_write(char __iomem *vport, unsigned char softcfg) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (sc6000_write(vport, WRITE_MDIRQ_CFG)) { 3018c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG); 3028c2ecf20Sopenharmony_ci return -EIO; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci if (sc6000_write(vport, softcfg)) { 3058c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n"); 3068c2ecf20Sopenharmony_ci return -EIO; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int sc6000_setup_board(char __iomem *vport, int config) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci int loop = 10; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci do { 3168c2ecf20Sopenharmony_ci if (sc6000_write(vport, COMMAND_88)) { 3178c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "CMD 0x%x: failed!\n", 3188c2ecf20Sopenharmony_ci COMMAND_88); 3198c2ecf20Sopenharmony_ci return -EIO; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci } while ((sc6000_wait_data(vport) < 0) && loop--); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (sc6000_read(vport) < 0) { 3248c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sc6000_read after CMD 0x%x: failed\n", 3258c2ecf20Sopenharmony_ci COMMAND_88); 3268c2ecf20Sopenharmony_ci return -EIO; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (sc6000_cfg_write(vport, config)) 3308c2ecf20Sopenharmony_ci return -ENODEV; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int sc6000_init_mss(char __iomem *vport, int config, 3368c2ecf20Sopenharmony_ci char __iomem *vmss_port, int mss_config) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci if (sc6000_write(vport, DSP_INIT_MSS)) { 3398c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sc6000_init_mss [0x%x]: failed!\n", 3408c2ecf20Sopenharmony_ci DSP_INIT_MSS); 3418c2ecf20Sopenharmony_ci return -EIO; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci msleep(10); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (sc6000_cfg_write(vport, config)) 3478c2ecf20Sopenharmony_ci return -EIO; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci iowrite8(mss_config, vmss_port); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic void sc6000_hw_cfg_encode(char __iomem *vport, int *cfg, 3558c2ecf20Sopenharmony_ci long xport, long xmpu, 3568c2ecf20Sopenharmony_ci long xmss_port, int joystick) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci cfg[0] = 0; 3598c2ecf20Sopenharmony_ci cfg[1] = 0; 3608c2ecf20Sopenharmony_ci if (xport == 0x240) 3618c2ecf20Sopenharmony_ci cfg[0] |= 1; 3628c2ecf20Sopenharmony_ci if (xmpu != SNDRV_AUTO_PORT) { 3638c2ecf20Sopenharmony_ci cfg[0] |= (xmpu & 0x30) >> 2; 3648c2ecf20Sopenharmony_ci cfg[1] |= 0x20; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci if (xmss_port == 0xe80) 3678c2ecf20Sopenharmony_ci cfg[0] |= 0x10; 3688c2ecf20Sopenharmony_ci cfg[0] |= 0x40; /* always set */ 3698c2ecf20Sopenharmony_ci if (!joystick) 3708c2ecf20Sopenharmony_ci cfg[0] |= 0x02; 3718c2ecf20Sopenharmony_ci cfg[1] |= 0x80; /* enable WSS system */ 3728c2ecf20Sopenharmony_ci cfg[1] &= ~0x40; /* disable IDE */ 3738c2ecf20Sopenharmony_ci snd_printd("hw cfg %x, %x\n", cfg[0], cfg[1]); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int sc6000_init_board(char __iomem *vport, 3778c2ecf20Sopenharmony_ci char __iomem *vmss_port, int dev) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci char answer[15]; 3808c2ecf20Sopenharmony_ci char version[2]; 3818c2ecf20Sopenharmony_ci int mss_config = sc6000_irq_to_softcfg(irq[dev]) | 3828c2ecf20Sopenharmony_ci sc6000_dma_to_softcfg(dma[dev]); 3838c2ecf20Sopenharmony_ci int config = mss_config | 3848c2ecf20Sopenharmony_ci sc6000_mpu_irq_to_softcfg(mpu_irq[dev]); 3858c2ecf20Sopenharmony_ci int err; 3868c2ecf20Sopenharmony_ci int old = 0; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci err = sc6000_dsp_reset(vport); 3898c2ecf20Sopenharmony_ci if (err < 0) { 3908c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sc6000_dsp_reset: failed!\n"); 3918c2ecf20Sopenharmony_ci return err; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci memset(answer, 0, sizeof(answer)); 3958c2ecf20Sopenharmony_ci err = sc6000_dsp_get_answer(vport, GET_DSP_COPYRIGHT, answer, 15); 3968c2ecf20Sopenharmony_ci if (err <= 0) { 3978c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sc6000_dsp_copyright: failed!\n"); 3988c2ecf20Sopenharmony_ci return -ENODEV; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci /* 4018c2ecf20Sopenharmony_ci * My SC-6000 card return "SC-6000" in DSPCopyright, so 4028c2ecf20Sopenharmony_ci * if we have something different, we have to be warned. 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_ci if (strncmp("SC-6000", answer, 7)) 4058c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n"); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (sc6000_dsp_get_answer(vport, GET_DSP_VERSION, version, 2) < 2) { 4088c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sc6000_dsp_version: failed!\n"); 4098c2ecf20Sopenharmony_ci return -ENODEV; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci printk(KERN_INFO PFX "Detected model: %s, DSP version %d.%d\n", 4128c2ecf20Sopenharmony_ci answer, version[0], version[1]); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* set configuration */ 4158c2ecf20Sopenharmony_ci sc6000_write(vport, COMMAND_5C); 4168c2ecf20Sopenharmony_ci if (sc6000_read(vport) < 0) 4178c2ecf20Sopenharmony_ci old = 1; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (!old) { 4208c2ecf20Sopenharmony_ci int cfg[2]; 4218c2ecf20Sopenharmony_ci sc6000_hw_cfg_encode(vport, &cfg[0], port[dev], mpu_port[dev], 4228c2ecf20Sopenharmony_ci mss_port[dev], joystick[dev]); 4238c2ecf20Sopenharmony_ci if (sc6000_hw_cfg_write(vport, cfg) < 0) { 4248c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sc6000_hw_cfg_write: failed!\n"); 4258c2ecf20Sopenharmony_ci return -EIO; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci err = sc6000_setup_board(vport, config); 4298c2ecf20Sopenharmony_ci if (err < 0) { 4308c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sc6000_setup_board: failed!\n"); 4318c2ecf20Sopenharmony_ci return -ENODEV; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci sc6000_dsp_reset(vport); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (!old) { 4378c2ecf20Sopenharmony_ci sc6000_write(vport, COMMAND_60); 4388c2ecf20Sopenharmony_ci sc6000_write(vport, 0x02); 4398c2ecf20Sopenharmony_ci sc6000_dsp_reset(vport); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci err = sc6000_setup_board(vport, config); 4438c2ecf20Sopenharmony_ci if (err < 0) { 4448c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "sc6000_setup_board: failed!\n"); 4458c2ecf20Sopenharmony_ci return -ENODEV; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci err = sc6000_init_mss(vport, config, vmss_port, mss_config); 4488c2ecf20Sopenharmony_ci if (err < 0) { 4498c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "Cannot initialize " 4508c2ecf20Sopenharmony_ci "Microsoft Sound System mode.\n"); 4518c2ecf20Sopenharmony_ci return -ENODEV; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci return 0; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic int snd_sc6000_mixer(struct snd_wss *chip) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct snd_card *card = chip->card; 4608c2ecf20Sopenharmony_ci struct snd_ctl_elem_id id1, id2; 4618c2ecf20Sopenharmony_ci int err; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci memset(&id1, 0, sizeof(id1)); 4648c2ecf20Sopenharmony_ci memset(&id2, 0, sizeof(id2)); 4658c2ecf20Sopenharmony_ci id1.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 4668c2ecf20Sopenharmony_ci id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 4678c2ecf20Sopenharmony_ci /* reassign AUX0 to FM */ 4688c2ecf20Sopenharmony_ci strcpy(id1.name, "Aux Playback Switch"); 4698c2ecf20Sopenharmony_ci strcpy(id2.name, "FM Playback Switch"); 4708c2ecf20Sopenharmony_ci err = snd_ctl_rename_id(card, &id1, &id2); 4718c2ecf20Sopenharmony_ci if (err < 0) 4728c2ecf20Sopenharmony_ci return err; 4738c2ecf20Sopenharmony_ci strcpy(id1.name, "Aux Playback Volume"); 4748c2ecf20Sopenharmony_ci strcpy(id2.name, "FM Playback Volume"); 4758c2ecf20Sopenharmony_ci err = snd_ctl_rename_id(card, &id1, &id2); 4768c2ecf20Sopenharmony_ci if (err < 0) 4778c2ecf20Sopenharmony_ci return err; 4788c2ecf20Sopenharmony_ci /* reassign AUX1 to CD */ 4798c2ecf20Sopenharmony_ci strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; 4808c2ecf20Sopenharmony_ci strcpy(id2.name, "CD Playback Switch"); 4818c2ecf20Sopenharmony_ci err = snd_ctl_rename_id(card, &id1, &id2); 4828c2ecf20Sopenharmony_ci if (err < 0) 4838c2ecf20Sopenharmony_ci return err; 4848c2ecf20Sopenharmony_ci strcpy(id1.name, "Aux Playback Volume"); 4858c2ecf20Sopenharmony_ci strcpy(id2.name, "CD Playback Volume"); 4868c2ecf20Sopenharmony_ci err = snd_ctl_rename_id(card, &id1, &id2); 4878c2ecf20Sopenharmony_ci if (err < 0) 4888c2ecf20Sopenharmony_ci return err; 4898c2ecf20Sopenharmony_ci return 0; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic int snd_sc6000_match(struct device *devptr, unsigned int dev) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci if (!enable[dev]) 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci if (port[dev] == SNDRV_AUTO_PORT) { 4978c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "specify IO port\n"); 4988c2ecf20Sopenharmony_ci return 0; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci if (mss_port[dev] == SNDRV_AUTO_PORT) { 5018c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "specify MSS port\n"); 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci if (port[dev] != 0x220 && port[dev] != 0x240) { 5058c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "Port must be 0x220 or 0x240\n"); 5068c2ecf20Sopenharmony_ci return 0; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci if (mss_port[dev] != 0x530 && mss_port[dev] != 0xe80) { 5098c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "MSS port must be 0x530 or 0xe80\n"); 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci if (irq[dev] != SNDRV_AUTO_IRQ && !sc6000_irq_to_softcfg(irq[dev])) { 5138c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "invalid IRQ %d\n", irq[dev]); 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci if (dma[dev] != SNDRV_AUTO_DMA && !sc6000_dma_to_softcfg(dma[dev])) { 5178c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "invalid DMA %d\n", dma[dev]); 5188c2ecf20Sopenharmony_ci return 0; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci if (mpu_port[dev] != SNDRV_AUTO_PORT && 5218c2ecf20Sopenharmony_ci (mpu_port[dev] & ~0x30L) != 0x300) { 5228c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "invalid MPU-401 port %lx\n", 5238c2ecf20Sopenharmony_ci mpu_port[dev]); 5248c2ecf20Sopenharmony_ci return 0; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci if (mpu_port[dev] != SNDRV_AUTO_PORT && 5278c2ecf20Sopenharmony_ci mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] != 0 && 5288c2ecf20Sopenharmony_ci !sc6000_mpu_irq_to_softcfg(mpu_irq[dev])) { 5298c2ecf20Sopenharmony_ci printk(KERN_ERR PFX "invalid MPU-401 IRQ %d\n", mpu_irq[dev]); 5308c2ecf20Sopenharmony_ci return 0; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci return 1; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic int snd_sc6000_probe(struct device *devptr, unsigned int dev) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci static const int possible_irqs[] = { 5, 7, 9, 10, 11, -1 }; 5388c2ecf20Sopenharmony_ci static const int possible_dmas[] = { 1, 3, 0, -1 }; 5398c2ecf20Sopenharmony_ci int err; 5408c2ecf20Sopenharmony_ci int xirq = irq[dev]; 5418c2ecf20Sopenharmony_ci int xdma = dma[dev]; 5428c2ecf20Sopenharmony_ci struct snd_card *card; 5438c2ecf20Sopenharmony_ci struct snd_wss *chip; 5448c2ecf20Sopenharmony_ci struct snd_opl3 *opl3; 5458c2ecf20Sopenharmony_ci char __iomem **vport; 5468c2ecf20Sopenharmony_ci char __iomem *vmss_port; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci err = snd_card_new(devptr, index[dev], id[dev], THIS_MODULE, 5508c2ecf20Sopenharmony_ci sizeof(vport), &card); 5518c2ecf20Sopenharmony_ci if (err < 0) 5528c2ecf20Sopenharmony_ci return err; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci vport = card->private_data; 5558c2ecf20Sopenharmony_ci if (xirq == SNDRV_AUTO_IRQ) { 5568c2ecf20Sopenharmony_ci xirq = snd_legacy_find_free_irq(possible_irqs); 5578c2ecf20Sopenharmony_ci if (xirq < 0) { 5588c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to find a free IRQ\n"); 5598c2ecf20Sopenharmony_ci err = -EBUSY; 5608c2ecf20Sopenharmony_ci goto err_exit; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (xdma == SNDRV_AUTO_DMA) { 5658c2ecf20Sopenharmony_ci xdma = snd_legacy_find_free_dma(possible_dmas); 5668c2ecf20Sopenharmony_ci if (xdma < 0) { 5678c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to find a free DMA\n"); 5688c2ecf20Sopenharmony_ci err = -EBUSY; 5698c2ecf20Sopenharmony_ci goto err_exit; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (!request_region(port[dev], 0x10, DRV_NAME)) { 5748c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX 5758c2ecf20Sopenharmony_ci "I/O port region is already in use.\n"); 5768c2ecf20Sopenharmony_ci err = -EBUSY; 5778c2ecf20Sopenharmony_ci goto err_exit; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci *vport = devm_ioport_map(devptr, port[dev], 0x10); 5808c2ecf20Sopenharmony_ci if (*vport == NULL) { 5818c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX 5828c2ecf20Sopenharmony_ci "I/O port cannot be iomapped.\n"); 5838c2ecf20Sopenharmony_ci err = -EBUSY; 5848c2ecf20Sopenharmony_ci goto err_unmap1; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* to make it marked as used */ 5888c2ecf20Sopenharmony_ci if (!request_region(mss_port[dev], 4, DRV_NAME)) { 5898c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX 5908c2ecf20Sopenharmony_ci "SC-6000 port I/O port region is already in use.\n"); 5918c2ecf20Sopenharmony_ci err = -EBUSY; 5928c2ecf20Sopenharmony_ci goto err_unmap1; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci vmss_port = devm_ioport_map(devptr, mss_port[dev], 4); 5958c2ecf20Sopenharmony_ci if (!vmss_port) { 5968c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX 5978c2ecf20Sopenharmony_ci "MSS port I/O cannot be iomapped.\n"); 5988c2ecf20Sopenharmony_ci err = -EBUSY; 5998c2ecf20Sopenharmony_ci goto err_unmap2; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci snd_printd("Initializing BASE[0x%lx] IRQ[%d] DMA[%d] MIRQ[%d]\n", 6038c2ecf20Sopenharmony_ci port[dev], xirq, xdma, 6048c2ecf20Sopenharmony_ci mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci err = sc6000_init_board(*vport, vmss_port, dev); 6078c2ecf20Sopenharmony_ci if (err < 0) 6088c2ecf20Sopenharmony_ci goto err_unmap2; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci err = snd_wss_create(card, mss_port[dev] + 4, -1, xirq, xdma, -1, 6118c2ecf20Sopenharmony_ci WSS_HW_DETECT, 0, &chip); 6128c2ecf20Sopenharmony_ci if (err < 0) 6138c2ecf20Sopenharmony_ci goto err_unmap2; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci err = snd_wss_pcm(chip, 0); 6168c2ecf20Sopenharmony_ci if (err < 0) { 6178c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX 6188c2ecf20Sopenharmony_ci "error creating new WSS PCM device\n"); 6198c2ecf20Sopenharmony_ci goto err_unmap2; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci err = snd_wss_mixer(chip); 6228c2ecf20Sopenharmony_ci if (err < 0) { 6238c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "error creating new WSS mixer\n"); 6248c2ecf20Sopenharmony_ci goto err_unmap2; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci err = snd_sc6000_mixer(chip); 6278c2ecf20Sopenharmony_ci if (err < 0) { 6288c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "the mixer rewrite failed\n"); 6298c2ecf20Sopenharmony_ci goto err_unmap2; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci if (snd_opl3_create(card, 6328c2ecf20Sopenharmony_ci 0x388, 0x388 + 2, 6338c2ecf20Sopenharmony_ci OPL3_HW_AUTO, 0, &opl3) < 0) { 6348c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "no OPL device at 0x%x-0x%x ?\n", 6358c2ecf20Sopenharmony_ci 0x388, 0x388 + 2); 6368c2ecf20Sopenharmony_ci } else { 6378c2ecf20Sopenharmony_ci err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); 6388c2ecf20Sopenharmony_ci if (err < 0) 6398c2ecf20Sopenharmony_ci goto err_unmap2; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (mpu_port[dev] != SNDRV_AUTO_PORT) { 6438c2ecf20Sopenharmony_ci if (mpu_irq[dev] == SNDRV_AUTO_IRQ) 6448c2ecf20Sopenharmony_ci mpu_irq[dev] = -1; 6458c2ecf20Sopenharmony_ci if (snd_mpu401_uart_new(card, 0, 6468c2ecf20Sopenharmony_ci MPU401_HW_MPU401, 6478c2ecf20Sopenharmony_ci mpu_port[dev], 0, 6488c2ecf20Sopenharmony_ci mpu_irq[dev], NULL) < 0) 6498c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "no MPU-401 device at 0x%lx ?\n", 6508c2ecf20Sopenharmony_ci mpu_port[dev]); 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci strcpy(card->driver, DRV_NAME); 6548c2ecf20Sopenharmony_ci strcpy(card->shortname, "SC-6000"); 6558c2ecf20Sopenharmony_ci sprintf(card->longname, "Gallant SC-6000 at 0x%lx, irq %d, dma %d", 6568c2ecf20Sopenharmony_ci mss_port[dev], xirq, xdma); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci err = snd_card_register(card); 6598c2ecf20Sopenharmony_ci if (err < 0) 6608c2ecf20Sopenharmony_ci goto err_unmap2; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci dev_set_drvdata(devptr, card); 6638c2ecf20Sopenharmony_ci return 0; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cierr_unmap2: 6668c2ecf20Sopenharmony_ci sc6000_setup_board(*vport, 0); 6678c2ecf20Sopenharmony_ci release_region(mss_port[dev], 4); 6688c2ecf20Sopenharmony_cierr_unmap1: 6698c2ecf20Sopenharmony_ci release_region(port[dev], 0x10); 6708c2ecf20Sopenharmony_cierr_exit: 6718c2ecf20Sopenharmony_ci snd_card_free(card); 6728c2ecf20Sopenharmony_ci return err; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic int snd_sc6000_remove(struct device *devptr, unsigned int dev) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(devptr); 6788c2ecf20Sopenharmony_ci char __iomem **vport = card->private_data; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (sc6000_setup_board(*vport, 0) < 0) 6818c2ecf20Sopenharmony_ci snd_printk(KERN_WARNING "sc6000_setup_board failed on exit!\n"); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci release_region(port[dev], 0x10); 6848c2ecf20Sopenharmony_ci release_region(mss_port[dev], 4); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci snd_card_free(card); 6878c2ecf20Sopenharmony_ci return 0; 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic struct isa_driver snd_sc6000_driver = { 6918c2ecf20Sopenharmony_ci .match = snd_sc6000_match, 6928c2ecf20Sopenharmony_ci .probe = snd_sc6000_probe, 6938c2ecf20Sopenharmony_ci .remove = snd_sc6000_remove, 6948c2ecf20Sopenharmony_ci /* FIXME: suspend/resume */ 6958c2ecf20Sopenharmony_ci .driver = { 6968c2ecf20Sopenharmony_ci .name = DRV_NAME, 6978c2ecf20Sopenharmony_ci }, 6988c2ecf20Sopenharmony_ci}; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_cimodule_isa_driver(snd_sc6000_driver, SNDRV_CARDS); 702