1e307258dSKrzysztof Helt /* 2e307258dSKrzysztof Helt * Driver for Gallant SC-6000 soundcard. This card is also known as 3e307258dSKrzysztof Helt * Audio Excel DSP 16 or Zoltrix AV302. 4e307258dSKrzysztof Helt * These cards use CompuMedia ASC-9308 chip + AD1848 codec. 5e307258dSKrzysztof Helt * 6e307258dSKrzysztof Helt * Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl> 7e307258dSKrzysztof Helt * 8e307258dSKrzysztof Helt * I don't have documentation for this card. I used the driver 9e307258dSKrzysztof Helt * for OSS/Free included in the kernel source as reference. 10e307258dSKrzysztof Helt * 11e307258dSKrzysztof Helt * This program is free software; you can redistribute it and/or modify 12e307258dSKrzysztof Helt * it under the terms of the GNU General Public License as published by 13e307258dSKrzysztof Helt * the Free Software Foundation; either version 2 of the License, or 14e307258dSKrzysztof Helt * (at your option) any later version. 15e307258dSKrzysztof Helt * 16e307258dSKrzysztof Helt * This program is distributed in the hope that it will be useful, 17e307258dSKrzysztof Helt * but WITHOUT ANY WARRANTY; without even the implied warranty of 18e307258dSKrzysztof Helt * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19e307258dSKrzysztof Helt * GNU General Public License for more details. 20e307258dSKrzysztof Helt * 21e307258dSKrzysztof Helt * You should have received a copy of the GNU General Public License 22e307258dSKrzysztof Helt * along with this program; if not, write to the Free Software 23e307258dSKrzysztof Helt * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 24e307258dSKrzysztof Helt */ 25e307258dSKrzysztof Helt 26e307258dSKrzysztof Helt #include <sound/driver.h> 27e307258dSKrzysztof Helt #include <linux/module.h> 28e307258dSKrzysztof Helt #include <linux/isa.h> 29e307258dSKrzysztof Helt #include <linux/io.h> 30e307258dSKrzysztof Helt #include <asm/dma.h> 31e307258dSKrzysztof Helt #include <sound/core.h> 32e307258dSKrzysztof Helt #include <sound/ad1848.h> 33e307258dSKrzysztof Helt #include <sound/opl3.h> 34e307258dSKrzysztof Helt #include <sound/mpu401.h> 35e307258dSKrzysztof Helt #include <sound/control.h> 36e307258dSKrzysztof Helt #define SNDRV_LEGACY_FIND_FREE_IRQ 37e307258dSKrzysztof Helt #define SNDRV_LEGACY_FIND_FREE_DMA 38e307258dSKrzysztof Helt #include <sound/initval.h> 39e307258dSKrzysztof Helt 40e307258dSKrzysztof Helt MODULE_AUTHOR("Krzysztof Helt"); 41e307258dSKrzysztof Helt MODULE_DESCRIPTION("Gallant SC-6000"); 42e307258dSKrzysztof Helt MODULE_LICENSE("GPL"); 43e307258dSKrzysztof Helt MODULE_SUPPORTED_DEVICE("{{Gallant, SC-6000}," 44e307258dSKrzysztof Helt "{AudioExcel, Audio Excel DSP 16}," 45e307258dSKrzysztof Helt "{Zoltrix, AV302}}"); 46e307258dSKrzysztof Helt 47e307258dSKrzysztof Helt static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 48e307258dSKrzysztof Helt static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 49e307258dSKrzysztof Helt static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ 50e307258dSKrzysztof Helt static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220, 0x240 */ 51e307258dSKrzysztof Helt static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5, 7, 9, 10, 11 */ 52e307258dSKrzysztof Helt static long mss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530, 0xe80 */ 53e307258dSKrzysztof Helt static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 54e307258dSKrzysztof Helt /* 0x300, 0x310, 0x320, 0x330 */ 55e307258dSKrzysztof Helt static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5, 7, 9, 10, 0 */ 56e307258dSKrzysztof Helt static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0, 1, 3 */ 57e307258dSKrzysztof Helt 58e307258dSKrzysztof Helt module_param_array(index, int, NULL, 0444); 59e307258dSKrzysztof Helt MODULE_PARM_DESC(index, "Index value for sc-6000 based soundcard."); 60e307258dSKrzysztof Helt module_param_array(id, charp, NULL, 0444); 61e307258dSKrzysztof Helt MODULE_PARM_DESC(id, "ID string for sc-6000 based soundcard."); 62e307258dSKrzysztof Helt module_param_array(enable, bool, NULL, 0444); 63e307258dSKrzysztof Helt MODULE_PARM_DESC(enable, "Enable sc-6000 based soundcard."); 64e307258dSKrzysztof Helt module_param_array(port, long, NULL, 0444); 65e307258dSKrzysztof Helt MODULE_PARM_DESC(port, "Port # for sc-6000 driver."); 66e307258dSKrzysztof Helt module_param_array(mss_port, long, NULL, 0444); 67e307258dSKrzysztof Helt MODULE_PARM_DESC(mss_port, "MSS Port # for sc-6000 driver."); 68e307258dSKrzysztof Helt module_param_array(mpu_port, long, NULL, 0444); 69e307258dSKrzysztof Helt MODULE_PARM_DESC(mpu_port, "MPU-401 port # for sc-6000 driver."); 70e307258dSKrzysztof Helt module_param_array(irq, int, NULL, 0444); 71e307258dSKrzysztof Helt MODULE_PARM_DESC(irq, "IRQ # for sc-6000 driver."); 72e307258dSKrzysztof Helt module_param_array(mpu_irq, int, NULL, 0444); 73e307258dSKrzysztof Helt MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver."); 74e307258dSKrzysztof Helt module_param_array(dma, int, NULL, 0444); 75e307258dSKrzysztof Helt MODULE_PARM_DESC(dma, "DMA # for sc-6000 driver."); 76e307258dSKrzysztof Helt 77e307258dSKrzysztof Helt /* 78e307258dSKrzysztof Helt * Commands of SC6000's DSP (SBPRO+special). 79e307258dSKrzysztof Helt * Some of them are COMMAND_xx, in the future they may change. 80e307258dSKrzysztof Helt */ 81e307258dSKrzysztof Helt #define WRITE_MDIRQ_CFG 0x50 /* Set M&I&DRQ mask (the real config) */ 82e307258dSKrzysztof Helt #define COMMAND_52 0x52 /* */ 83e307258dSKrzysztof Helt #define READ_HARD_CFG 0x58 /* Read Hardware Config (I/O base etc) */ 84e307258dSKrzysztof Helt #define COMMAND_5C 0x5c /* */ 85e307258dSKrzysztof Helt #define COMMAND_60 0x60 /* */ 86e307258dSKrzysztof Helt #define COMMAND_66 0x66 /* */ 87e307258dSKrzysztof Helt #define COMMAND_6C 0x6c /* */ 88e307258dSKrzysztof Helt #define COMMAND_6E 0x6e /* */ 89e307258dSKrzysztof Helt #define COMMAND_88 0x88 /* Unknown command */ 90e307258dSKrzysztof Helt #define DSP_INIT_MSS 0x8c /* Enable Microsoft Sound System mode */ 91e307258dSKrzysztof Helt #define COMMAND_C5 0xc5 /* */ 92e307258dSKrzysztof Helt #define GET_DSP_VERSION 0xe1 /* Get DSP Version */ 93e307258dSKrzysztof Helt #define GET_DSP_COPYRIGHT 0xe3 /* Get DSP Copyright */ 94e307258dSKrzysztof Helt 95e307258dSKrzysztof Helt /* 96e307258dSKrzysztof Helt * Offsets of SC6000 DSP I/O ports. The offset is added to base I/O port 97e307258dSKrzysztof Helt * to have the actual I/O port. 98e307258dSKrzysztof Helt * Register permissions are: 99e307258dSKrzysztof Helt * (wo) == Write Only 100e307258dSKrzysztof Helt * (ro) == Read Only 101e307258dSKrzysztof Helt * (w-) == Write 102e307258dSKrzysztof Helt * (r-) == Read 103e307258dSKrzysztof Helt */ 104e307258dSKrzysztof Helt #define DSP_RESET 0x06 /* offset of DSP RESET (wo) */ 105e307258dSKrzysztof Helt #define DSP_READ 0x0a /* offset of DSP READ (ro) */ 106e307258dSKrzysztof Helt #define DSP_WRITE 0x0c /* offset of DSP WRITE (w-) */ 107e307258dSKrzysztof Helt #define DSP_COMMAND 0x0c /* offset of DSP COMMAND (w-) */ 108e307258dSKrzysztof Helt #define DSP_STATUS 0x0c /* offset of DSP STATUS (r-) */ 109e307258dSKrzysztof Helt #define DSP_DATAVAIL 0x0e /* offset of DSP DATA AVAILABLE (ro) */ 110e307258dSKrzysztof Helt 111e307258dSKrzysztof Helt #define PFX "sc6000: " 112e307258dSKrzysztof Helt #define DRV_NAME "SC-6000" 113e307258dSKrzysztof Helt 114e307258dSKrzysztof Helt /* hardware dependent functions */ 115e307258dSKrzysztof Helt 116e307258dSKrzysztof Helt /* 117e307258dSKrzysztof Helt * sc6000_irq_to_softcfg - Decode irq number into cfg code. 118e307258dSKrzysztof Helt */ 119e307258dSKrzysztof Helt static __devinit unsigned char sc6000_irq_to_softcfg(int irq) 120e307258dSKrzysztof Helt { 121e307258dSKrzysztof Helt unsigned char val = 0; 122e307258dSKrzysztof Helt 123e307258dSKrzysztof Helt switch (irq) { 124e307258dSKrzysztof Helt case 5: 125e307258dSKrzysztof Helt val = 0x28; 126e307258dSKrzysztof Helt break; 127e307258dSKrzysztof Helt case 7: 128e307258dSKrzysztof Helt val = 0x8; 129e307258dSKrzysztof Helt break; 130e307258dSKrzysztof Helt case 9: 131e307258dSKrzysztof Helt val = 0x10; 132e307258dSKrzysztof Helt break; 133e307258dSKrzysztof Helt case 10: 134e307258dSKrzysztof Helt val = 0x18; 135e307258dSKrzysztof Helt break; 136e307258dSKrzysztof Helt case 11: 137e307258dSKrzysztof Helt val = 0x20; 138e307258dSKrzysztof Helt break; 139e307258dSKrzysztof Helt default: 140e307258dSKrzysztof Helt break; 141e307258dSKrzysztof Helt } 142e307258dSKrzysztof Helt return val; 143e307258dSKrzysztof Helt } 144e307258dSKrzysztof Helt 145e307258dSKrzysztof Helt /* 146e307258dSKrzysztof Helt * sc6000_dma_to_softcfg - Decode dma number into cfg code. 147e307258dSKrzysztof Helt */ 148e307258dSKrzysztof Helt static __devinit unsigned char sc6000_dma_to_softcfg(int dma) 149e307258dSKrzysztof Helt { 150e307258dSKrzysztof Helt unsigned char val = 0; 151e307258dSKrzysztof Helt 152e307258dSKrzysztof Helt switch (dma) { 153e307258dSKrzysztof Helt case 0: 154e307258dSKrzysztof Helt val = 1; 155e307258dSKrzysztof Helt break; 156e307258dSKrzysztof Helt case 1: 157e307258dSKrzysztof Helt val = 2; 158e307258dSKrzysztof Helt break; 159e307258dSKrzysztof Helt case 3: 160e307258dSKrzysztof Helt val = 3; 161e307258dSKrzysztof Helt break; 162e307258dSKrzysztof Helt default: 163e307258dSKrzysztof Helt break; 164e307258dSKrzysztof Helt } 165e307258dSKrzysztof Helt return val; 166e307258dSKrzysztof Helt } 167e307258dSKrzysztof Helt 168e307258dSKrzysztof Helt /* 169e307258dSKrzysztof Helt * sc6000_mpu_irq_to_softcfg - Decode MPU-401 irq number into cfg code. 170e307258dSKrzysztof Helt */ 171e307258dSKrzysztof Helt static __devinit unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq) 172e307258dSKrzysztof Helt { 173e307258dSKrzysztof Helt unsigned char val = 0; 174e307258dSKrzysztof Helt 175e307258dSKrzysztof Helt switch (mpu_irq) { 176e307258dSKrzysztof Helt case 5: 177e307258dSKrzysztof Helt val = 4; 178e307258dSKrzysztof Helt break; 179e307258dSKrzysztof Helt case 7: 180e307258dSKrzysztof Helt val = 0x44; 181e307258dSKrzysztof Helt break; 182e307258dSKrzysztof Helt case 9: 183e307258dSKrzysztof Helt val = 0x84; 184e307258dSKrzysztof Helt break; 185e307258dSKrzysztof Helt case 10: 186e307258dSKrzysztof Helt val = 0xc4; 187e307258dSKrzysztof Helt break; 188e307258dSKrzysztof Helt default: 189e307258dSKrzysztof Helt break; 190e307258dSKrzysztof Helt } 191e307258dSKrzysztof Helt return val; 192e307258dSKrzysztof Helt } 193e307258dSKrzysztof Helt 194e307258dSKrzysztof Helt static __devinit int sc6000_wait_data(char __iomem *vport) 195e307258dSKrzysztof Helt { 196e307258dSKrzysztof Helt int loop = 1000; 197e307258dSKrzysztof Helt unsigned char val = 0; 198e307258dSKrzysztof Helt 199e307258dSKrzysztof Helt do { 200e307258dSKrzysztof Helt val = ioread8(vport + DSP_DATAVAIL); 201e307258dSKrzysztof Helt if (val & 0x80) 202e307258dSKrzysztof Helt return 0; 203e307258dSKrzysztof Helt cpu_relax(); 204e307258dSKrzysztof Helt } while (loop--); 205e307258dSKrzysztof Helt 206e307258dSKrzysztof Helt return -EAGAIN; 207e307258dSKrzysztof Helt } 208e307258dSKrzysztof Helt 209e307258dSKrzysztof Helt static __devinit int sc6000_read(char __iomem *vport) 210e307258dSKrzysztof Helt { 211e307258dSKrzysztof Helt if (sc6000_wait_data(vport)) 212e307258dSKrzysztof Helt return -EBUSY; 213e307258dSKrzysztof Helt 214e307258dSKrzysztof Helt return ioread8(vport + DSP_READ); 215e307258dSKrzysztof Helt 216e307258dSKrzysztof Helt } 217e307258dSKrzysztof Helt 218e307258dSKrzysztof Helt static __devinit int sc6000_write(char __iomem *vport, int cmd) 219e307258dSKrzysztof Helt { 220e307258dSKrzysztof Helt unsigned char val; 221e307258dSKrzysztof Helt int loop = 500000; 222e307258dSKrzysztof Helt 223e307258dSKrzysztof Helt do { 224e307258dSKrzysztof Helt val = ioread8(vport + DSP_STATUS); 225e307258dSKrzysztof Helt /* 226e307258dSKrzysztof Helt * DSP ready to receive data if bit 7 of val == 0 227e307258dSKrzysztof Helt */ 228e307258dSKrzysztof Helt if (!(val & 0x80)) { 229e307258dSKrzysztof Helt iowrite8(cmd, vport + DSP_COMMAND); 230e307258dSKrzysztof Helt return 0; 231e307258dSKrzysztof Helt } 232e307258dSKrzysztof Helt cpu_relax(); 233e307258dSKrzysztof Helt } while (loop--); 234e307258dSKrzysztof Helt 235e307258dSKrzysztof Helt snd_printk(KERN_ERR "DSP Command (0x%x) timeout.\n", cmd); 236e307258dSKrzysztof Helt 237e307258dSKrzysztof Helt return -EIO; 238e307258dSKrzysztof Helt } 239e307258dSKrzysztof Helt 240e307258dSKrzysztof Helt static int __devinit sc6000_dsp_get_answer(char __iomem *vport, int command, 241e307258dSKrzysztof Helt char *data, int data_len) 242e307258dSKrzysztof Helt { 243e307258dSKrzysztof Helt int len = 0; 244e307258dSKrzysztof Helt 245e307258dSKrzysztof Helt if (sc6000_write(vport, command)) { 246e307258dSKrzysztof Helt snd_printk(KERN_ERR "CMD 0x%x: failed!\n", command); 247e307258dSKrzysztof Helt return -EIO; 248e307258dSKrzysztof Helt } 249e307258dSKrzysztof Helt 250e307258dSKrzysztof Helt do { 251e307258dSKrzysztof Helt int val = sc6000_read(vport); 252e307258dSKrzysztof Helt 253e307258dSKrzysztof Helt if (val < 0) 254e307258dSKrzysztof Helt break; 255e307258dSKrzysztof Helt 256e307258dSKrzysztof Helt data[len++] = val; 257e307258dSKrzysztof Helt 258e307258dSKrzysztof Helt } while (len < data_len); 259e307258dSKrzysztof Helt 260e307258dSKrzysztof Helt /* 261e307258dSKrzysztof Helt * If no more data available, return to the caller, no error if len>0. 262e307258dSKrzysztof Helt * We have no other way to know when the string is finished. 263e307258dSKrzysztof Helt */ 264e307258dSKrzysztof Helt return len ? len : -EIO; 265e307258dSKrzysztof Helt } 266e307258dSKrzysztof Helt 267e307258dSKrzysztof Helt static int __devinit sc6000_dsp_reset(char __iomem *vport) 268e307258dSKrzysztof Helt { 269e307258dSKrzysztof Helt iowrite8(1, vport + DSP_RESET); 270e307258dSKrzysztof Helt udelay(10); 271e307258dSKrzysztof Helt iowrite8(0, vport + DSP_RESET); 272e307258dSKrzysztof Helt udelay(20); 273e307258dSKrzysztof Helt if (sc6000_read(vport) == 0xaa) 274e307258dSKrzysztof Helt return 0; 275e307258dSKrzysztof Helt return -ENODEV; 276e307258dSKrzysztof Helt } 277e307258dSKrzysztof Helt 278e307258dSKrzysztof Helt /* detection and initialization */ 279e307258dSKrzysztof Helt static int __devinit sc6000_cfg_write(char __iomem *vport, 280e307258dSKrzysztof Helt unsigned char softcfg) 281e307258dSKrzysztof Helt { 282e307258dSKrzysztof Helt 283e307258dSKrzysztof Helt if (sc6000_write(vport, WRITE_MDIRQ_CFG)) { 284e307258dSKrzysztof Helt snd_printk(KERN_ERR "CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG); 285e307258dSKrzysztof Helt return -EIO; 286e307258dSKrzysztof Helt } 287e307258dSKrzysztof Helt if (sc6000_write(vport, softcfg)) { 288e307258dSKrzysztof Helt snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n"); 289e307258dSKrzysztof Helt return -EIO; 290e307258dSKrzysztof Helt } 291e307258dSKrzysztof Helt return 0; 292e307258dSKrzysztof Helt } 293e307258dSKrzysztof Helt 294e307258dSKrzysztof Helt static int __devinit sc6000_setup_board(char __iomem *vport, int config) 295e307258dSKrzysztof Helt { 296e307258dSKrzysztof Helt int loop = 10; 297e307258dSKrzysztof Helt 298e307258dSKrzysztof Helt do { 299e307258dSKrzysztof Helt if (sc6000_write(vport, COMMAND_88)) { 300e307258dSKrzysztof Helt snd_printk(KERN_ERR "CMD 0x%x: failed!\n", 301e307258dSKrzysztof Helt COMMAND_88); 302e307258dSKrzysztof Helt return -EIO; 303e307258dSKrzysztof Helt } 304e307258dSKrzysztof Helt } while ((sc6000_wait_data(vport) < 0) && loop--); 305e307258dSKrzysztof Helt 306e307258dSKrzysztof Helt if (sc6000_read(vport) < 0) { 307e307258dSKrzysztof Helt snd_printk(KERN_ERR "sc6000_read after CMD 0x%x: failed\n", 308e307258dSKrzysztof Helt COMMAND_88); 309e307258dSKrzysztof Helt return -EIO; 310e307258dSKrzysztof Helt } 311e307258dSKrzysztof Helt 312e307258dSKrzysztof Helt if (sc6000_cfg_write(vport, config)) 313e307258dSKrzysztof Helt return -ENODEV; 314e307258dSKrzysztof Helt 315e307258dSKrzysztof Helt return 0; 316e307258dSKrzysztof Helt } 317e307258dSKrzysztof Helt 318e307258dSKrzysztof Helt static int __devinit sc6000_init_mss(char __iomem *vport, int config, 319e307258dSKrzysztof Helt char __iomem *vmss_port, int mss_config) 320e307258dSKrzysztof Helt { 321e307258dSKrzysztof Helt if (sc6000_write(vport, DSP_INIT_MSS)) { 322e307258dSKrzysztof Helt snd_printk(KERN_ERR "sc6000_init_mss [0x%x]: failed!\n", 323e307258dSKrzysztof Helt DSP_INIT_MSS); 324e307258dSKrzysztof Helt return -EIO; 325e307258dSKrzysztof Helt } 326e307258dSKrzysztof Helt 327e307258dSKrzysztof Helt msleep(10); 328e307258dSKrzysztof Helt 329e307258dSKrzysztof Helt if (sc6000_cfg_write(vport, config)) 330e307258dSKrzysztof Helt return -EIO; 331e307258dSKrzysztof Helt 332e307258dSKrzysztof Helt iowrite8(mss_config, vmss_port); 333e307258dSKrzysztof Helt 334e307258dSKrzysztof Helt return 0; 335e307258dSKrzysztof Helt } 336e307258dSKrzysztof Helt 337e307258dSKrzysztof Helt static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma, 338e307258dSKrzysztof Helt char __iomem *vmss_port, int mpu_irq) 339e307258dSKrzysztof Helt { 340e307258dSKrzysztof Helt char answer[15]; 341e307258dSKrzysztof Helt char version[2]; 342e307258dSKrzysztof Helt int mss_config = sc6000_irq_to_softcfg(irq) | 343e307258dSKrzysztof Helt sc6000_dma_to_softcfg(dma); 344e307258dSKrzysztof Helt int config = mss_config | 345e307258dSKrzysztof Helt sc6000_mpu_irq_to_softcfg(mpu_irq); 346e307258dSKrzysztof Helt int err; 347e307258dSKrzysztof Helt 348e307258dSKrzysztof Helt err = sc6000_dsp_reset(vport); 349e307258dSKrzysztof Helt if (err < 0) { 350e307258dSKrzysztof Helt snd_printk(KERN_ERR "sc6000_dsp_reset: failed!\n"); 351e307258dSKrzysztof Helt return err; 352e307258dSKrzysztof Helt } 353e307258dSKrzysztof Helt 354e307258dSKrzysztof Helt err = sc6000_dsp_get_answer(vport, GET_DSP_COPYRIGHT, answer, 15); 355e307258dSKrzysztof Helt if (err <= 0) { 356e307258dSKrzysztof Helt snd_printk(KERN_ERR "sc6000_dsp_copyright: failed!\n"); 357e307258dSKrzysztof Helt return -ENODEV; 358e307258dSKrzysztof Helt } 359e307258dSKrzysztof Helt /* 360e307258dSKrzysztof Helt * My SC-6000 card return "SC-6000" in DSPCopyright, so 361e307258dSKrzysztof Helt * if we have something different, we have to be warned. 362e307258dSKrzysztof Helt * Mine returns "SC-6000A " - KH 363e307258dSKrzysztof Helt */ 364e307258dSKrzysztof Helt if (strncmp("SC-6000", answer, 7)) 365e307258dSKrzysztof Helt snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n"); 366e307258dSKrzysztof Helt 367e307258dSKrzysztof Helt if (sc6000_dsp_get_answer(vport, GET_DSP_VERSION, version, 2) < 2) { 368e307258dSKrzysztof Helt snd_printk(KERN_ERR "sc6000_dsp_version: failed!\n"); 369e307258dSKrzysztof Helt return -ENODEV; 370e307258dSKrzysztof Helt } 371e307258dSKrzysztof Helt printk(KERN_INFO PFX "Detected model: %s, DSP version %d.%d\n", 372e307258dSKrzysztof Helt answer, version[0], version[1]); 373e307258dSKrzysztof Helt 374e307258dSKrzysztof Helt /* 375e307258dSKrzysztof Helt * 0x0A == (IRQ 7, DMA 1, MIRQ 0) 376e307258dSKrzysztof Helt */ 377e307258dSKrzysztof Helt err = sc6000_cfg_write(vport, 0x0a); 378e307258dSKrzysztof Helt if (err < 0) { 379e307258dSKrzysztof Helt snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n"); 380e307258dSKrzysztof Helt return -EFAULT; 381e307258dSKrzysztof Helt } 382e307258dSKrzysztof Helt 383e307258dSKrzysztof Helt err = sc6000_setup_board(vport, config); 384e307258dSKrzysztof Helt if (err < 0) { 385e307258dSKrzysztof Helt snd_printk(KERN_ERR "sc6000_setup_board: failed!\n"); 386e307258dSKrzysztof Helt return -ENODEV; 387e307258dSKrzysztof Helt } 388e307258dSKrzysztof Helt 389e307258dSKrzysztof Helt err = sc6000_init_mss(vport, config, vmss_port, mss_config); 390e307258dSKrzysztof Helt if (err < 0) { 391e307258dSKrzysztof Helt snd_printk(KERN_ERR "Can not initialize" 392e307258dSKrzysztof Helt "Microsoft Sound System mode.\n"); 393e307258dSKrzysztof Helt return -ENODEV; 394e307258dSKrzysztof Helt } 395e307258dSKrzysztof Helt 396e307258dSKrzysztof Helt return 0; 397e307258dSKrzysztof Helt } 398e307258dSKrzysztof Helt 399e307258dSKrzysztof Helt static int __devinit snd_sc6000_mixer(struct snd_ad1848 *chip) 400e307258dSKrzysztof Helt { 401e307258dSKrzysztof Helt struct snd_card *card = chip->card; 402e307258dSKrzysztof Helt struct snd_ctl_elem_id id1, id2; 403e307258dSKrzysztof Helt int err; 404e307258dSKrzysztof Helt 405e307258dSKrzysztof Helt memset(&id1, 0, sizeof(id1)); 406e307258dSKrzysztof Helt memset(&id2, 0, sizeof(id2)); 407e307258dSKrzysztof Helt id1.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 408e307258dSKrzysztof Helt id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 409e307258dSKrzysztof Helt /* reassign AUX0 to FM */ 410e307258dSKrzysztof Helt strcpy(id1.name, "Aux Playback Switch"); 411e307258dSKrzysztof Helt strcpy(id2.name, "FM Playback Switch"); 412e307258dSKrzysztof Helt err = snd_ctl_rename_id(card, &id1, &id2); 413e307258dSKrzysztof Helt if (err < 0) 414e307258dSKrzysztof Helt return err; 415e307258dSKrzysztof Helt strcpy(id1.name, "Aux Playback Volume"); 416e307258dSKrzysztof Helt strcpy(id2.name, "FM Playback Volume"); 417e307258dSKrzysztof Helt err = snd_ctl_rename_id(card, &id1, &id2); 418e307258dSKrzysztof Helt if (err < 0) 419e307258dSKrzysztof Helt return err; 420e307258dSKrzysztof Helt /* reassign AUX1 to CD */ 421e307258dSKrzysztof Helt strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; 422e307258dSKrzysztof Helt strcpy(id2.name, "CD Playback Switch"); 423e307258dSKrzysztof Helt err = snd_ctl_rename_id(card, &id1, &id2); 424e307258dSKrzysztof Helt if (err < 0) 425e307258dSKrzysztof Helt return err; 426e307258dSKrzysztof Helt strcpy(id1.name, "Aux Playback Volume"); 427e307258dSKrzysztof Helt strcpy(id2.name, "CD Playback Volume"); 428e307258dSKrzysztof Helt err = snd_ctl_rename_id(card, &id1, &id2); 429e307258dSKrzysztof Helt if (err < 0) 430e307258dSKrzysztof Helt return err; 431e307258dSKrzysztof Helt return 0; 432e307258dSKrzysztof Helt } 433e307258dSKrzysztof Helt 434e307258dSKrzysztof Helt static int __devinit snd_sc6000_match(struct device *devptr, unsigned int dev) 435e307258dSKrzysztof Helt { 436e307258dSKrzysztof Helt if (!enable[dev]) 437e307258dSKrzysztof Helt return 0; 438e307258dSKrzysztof Helt if (port[dev] == SNDRV_AUTO_PORT) { 439e307258dSKrzysztof Helt printk(KERN_ERR PFX "specify IO port\n"); 440e307258dSKrzysztof Helt return 0; 441e307258dSKrzysztof Helt } 442e307258dSKrzysztof Helt if (mss_port[dev] == SNDRV_AUTO_PORT) { 443e307258dSKrzysztof Helt printk(KERN_ERR PFX "specify MSS port\n"); 444e307258dSKrzysztof Helt return 0; 445e307258dSKrzysztof Helt } 446e307258dSKrzysztof Helt if (port[dev] != 0x220 && port[dev] != 0x240) { 447e307258dSKrzysztof Helt printk(KERN_ERR PFX "Port must be 0x220 or 0x240\n"); 448e307258dSKrzysztof Helt return 0; 449e307258dSKrzysztof Helt } 450e307258dSKrzysztof Helt if (mss_port[dev] != 0x530 && mss_port[dev] != 0xe80) { 451e307258dSKrzysztof Helt printk(KERN_ERR PFX "MSS port must be 0x530 or 0xe80\n"); 452e307258dSKrzysztof Helt return 0; 453e307258dSKrzysztof Helt } 454e307258dSKrzysztof Helt if (irq[dev] != SNDRV_AUTO_IRQ && !sc6000_irq_to_softcfg(irq[dev])) { 455e307258dSKrzysztof Helt printk(KERN_ERR PFX "invalid IRQ %d\n", irq[dev]); 456e307258dSKrzysztof Helt return 0; 457e307258dSKrzysztof Helt } 458e307258dSKrzysztof Helt if (dma[dev] != SNDRV_AUTO_DMA && !sc6000_dma_to_softcfg(dma[dev])) { 459e307258dSKrzysztof Helt printk(KERN_ERR PFX "invalid DMA %d\n", dma[dev]); 460e307258dSKrzysztof Helt return 0; 461e307258dSKrzysztof Helt } 462e307258dSKrzysztof Helt if (mpu_port[dev] != SNDRV_AUTO_PORT && 463e307258dSKrzysztof Helt (mpu_port[dev] & ~0x30L) != 0x300) { 464e307258dSKrzysztof Helt printk(KERN_ERR PFX "invalid MPU-401 port %lx\n", 465e307258dSKrzysztof Helt mpu_port[dev]); 466e307258dSKrzysztof Helt return 0; 467e307258dSKrzysztof Helt } 468e307258dSKrzysztof Helt if (mpu_port[dev] != SNDRV_AUTO_PORT && 469e307258dSKrzysztof Helt mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] != 0 && 470e307258dSKrzysztof Helt !sc6000_mpu_irq_to_softcfg(mpu_irq[dev])) { 471e307258dSKrzysztof Helt printk(KERN_ERR PFX "invalid MPU-401 IRQ %d\n", mpu_irq[dev]); 472e307258dSKrzysztof Helt return 0; 473e307258dSKrzysztof Helt } 474e307258dSKrzysztof Helt return 1; 475e307258dSKrzysztof Helt } 476e307258dSKrzysztof Helt 477e307258dSKrzysztof Helt static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev) 478e307258dSKrzysztof Helt { 479e307258dSKrzysztof Helt static int possible_irqs[] = { 5, 7, 9, 10, 11, -1 }; 480e307258dSKrzysztof Helt static int possible_dmas[] = { 1, 3, 0, -1 }; 481e307258dSKrzysztof Helt int err; 482e307258dSKrzysztof Helt int xirq = irq[dev]; 483e307258dSKrzysztof Helt int xdma = dma[dev]; 484e307258dSKrzysztof Helt struct snd_card *card; 485e307258dSKrzysztof Helt struct snd_ad1848 *chip; 486e307258dSKrzysztof Helt struct snd_opl3 *opl3; 487e307258dSKrzysztof Helt char __iomem *vport; 488e307258dSKrzysztof Helt char __iomem *vmss_port; 489e307258dSKrzysztof Helt 490e307258dSKrzysztof Helt 491e307258dSKrzysztof Helt card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); 492e307258dSKrzysztof Helt if (!card) 493e307258dSKrzysztof Helt return -ENOMEM; 494e307258dSKrzysztof Helt 495e307258dSKrzysztof Helt if (xirq == SNDRV_AUTO_IRQ) { 496e307258dSKrzysztof Helt xirq = snd_legacy_find_free_irq(possible_irqs); 497e307258dSKrzysztof Helt if (xirq < 0) { 498e307258dSKrzysztof Helt snd_printk(KERN_ERR PFX "unable to find a free IRQ\n"); 499e307258dSKrzysztof Helt err = -EBUSY; 500e307258dSKrzysztof Helt goto err_exit; 501e307258dSKrzysztof Helt } 502e307258dSKrzysztof Helt } 503e307258dSKrzysztof Helt 504e307258dSKrzysztof Helt if (xdma == SNDRV_AUTO_DMA) { 505e307258dSKrzysztof Helt xdma = snd_legacy_find_free_dma(possible_dmas); 506e307258dSKrzysztof Helt if (xdma < 0) { 507e307258dSKrzysztof Helt snd_printk(KERN_ERR PFX "unable to find a free DMA\n"); 508e307258dSKrzysztof Helt err = -EBUSY; 509e307258dSKrzysztof Helt goto err_exit; 510e307258dSKrzysztof Helt } 511e307258dSKrzysztof Helt } 512e307258dSKrzysztof Helt 513e307258dSKrzysztof Helt if (!request_region(port[dev], 0x10, DRV_NAME)) { 514e307258dSKrzysztof Helt snd_printk(KERN_ERR PFX 515e307258dSKrzysztof Helt "I/O port region is already in use.\n"); 516e307258dSKrzysztof Helt err = -EBUSY; 517e307258dSKrzysztof Helt goto err_exit; 518e307258dSKrzysztof Helt } 519e307258dSKrzysztof Helt vport = devm_ioport_map(devptr, port[dev], 0x10); 520e307258dSKrzysztof Helt if (!vport) { 521e307258dSKrzysztof Helt snd_printk(KERN_ERR PFX 522e307258dSKrzysztof Helt "I/O port cannot be iomaped.\n"); 523e307258dSKrzysztof Helt err = -EBUSY; 524e307258dSKrzysztof Helt goto err_unmap1; 525e307258dSKrzysztof Helt } 526e307258dSKrzysztof Helt 527e307258dSKrzysztof Helt /* to make it marked as used */ 528e307258dSKrzysztof Helt if (!request_region(mss_port[dev], 4, DRV_NAME)) { 529e307258dSKrzysztof Helt snd_printk(KERN_ERR PFX 530e307258dSKrzysztof Helt "SC-6000 port I/O port region is already in use.\n"); 531e307258dSKrzysztof Helt err = -EBUSY; 532e307258dSKrzysztof Helt goto err_unmap2; 533e307258dSKrzysztof Helt } 534e307258dSKrzysztof Helt vmss_port = devm_ioport_map(devptr, mss_port[dev], 4); 535e307258dSKrzysztof Helt if (!vport) { 536e307258dSKrzysztof Helt snd_printk(KERN_ERR PFX 537e307258dSKrzysztof Helt "MSS port I/O cannot be iomaped.\n"); 538e307258dSKrzysztof Helt err = -EBUSY; 539e307258dSKrzysztof Helt goto err_unmap2; 540e307258dSKrzysztof Helt } 541e307258dSKrzysztof Helt 542e307258dSKrzysztof Helt snd_printd("Initializing BASE[0x%lx] IRQ[%d] DMA[%d] MIRQ[%d]\n", 543e307258dSKrzysztof Helt port[dev], xirq, xdma, 544e307258dSKrzysztof Helt mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]); 545e307258dSKrzysztof Helt 546e307258dSKrzysztof Helt err = sc6000_init_board(vport, xirq, xdma, vmss_port, mpu_irq[dev]); 547e307258dSKrzysztof Helt if (err < 0) 548e307258dSKrzysztof Helt goto err_unmap2; 549e307258dSKrzysztof Helt 550e307258dSKrzysztof Helt err = snd_ad1848_create(card, mss_port[dev] + 4, xirq, xdma, 551e307258dSKrzysztof Helt AD1848_HW_DETECT, &chip); 552e307258dSKrzysztof Helt if (err < 0) 553e307258dSKrzysztof Helt goto err_unmap2; 554e307258dSKrzysztof Helt card->private_data = chip; 555e307258dSKrzysztof Helt 556e307258dSKrzysztof Helt err = snd_ad1848_pcm(chip, 0, NULL); 557e307258dSKrzysztof Helt if (err < 0) { 558e307258dSKrzysztof Helt snd_printk(KERN_ERR PFX 559e307258dSKrzysztof Helt "error creating new ad1848 PCM device\n"); 560e307258dSKrzysztof Helt goto err_unmap2; 561e307258dSKrzysztof Helt } 562e307258dSKrzysztof Helt err = snd_ad1848_mixer(chip); 563e307258dSKrzysztof Helt if (err < 0) { 564e307258dSKrzysztof Helt snd_printk(KERN_ERR PFX "error creating new ad1848 mixer\n"); 565e307258dSKrzysztof Helt goto err_unmap2; 566e307258dSKrzysztof Helt } 567e307258dSKrzysztof Helt err = snd_sc6000_mixer(chip); 568e307258dSKrzysztof Helt if (err < 0) { 569e307258dSKrzysztof Helt snd_printk(KERN_ERR PFX "the mixer rewrite failed\n"); 570e307258dSKrzysztof Helt goto err_unmap2; 571e307258dSKrzysztof Helt } 572e307258dSKrzysztof Helt if (snd_opl3_create(card, 573e307258dSKrzysztof Helt 0x388, 0x388 + 2, 574e307258dSKrzysztof Helt OPL3_HW_AUTO, 0, &opl3) < 0) { 575e307258dSKrzysztof Helt snd_printk(KERN_ERR PFX "no OPL device at 0x%x-0x%x ?\n", 576e307258dSKrzysztof Helt 0x388, 0x388 + 2); 577e307258dSKrzysztof Helt } else { 578e307258dSKrzysztof Helt err = snd_opl3_timer_new(opl3, 0, 1); 579e307258dSKrzysztof Helt if (err < 0) 580e307258dSKrzysztof Helt goto err_unmap2; 581e307258dSKrzysztof Helt 582e307258dSKrzysztof Helt err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); 583e307258dSKrzysztof Helt if (err < 0) 584e307258dSKrzysztof Helt goto err_unmap2; 585e307258dSKrzysztof Helt } 586e307258dSKrzysztof Helt 587e307258dSKrzysztof Helt if (mpu_port[dev] != SNDRV_AUTO_PORT) { 588e307258dSKrzysztof Helt if (mpu_irq[dev] == SNDRV_AUTO_IRQ) 589e307258dSKrzysztof Helt mpu_irq[dev] = -1; 590e307258dSKrzysztof Helt if (snd_mpu401_uart_new(card, 0, 591e307258dSKrzysztof Helt MPU401_HW_MPU401, 592e307258dSKrzysztof Helt mpu_port[dev], 0, 593e307258dSKrzysztof Helt mpu_irq[dev], IRQF_DISABLED, 594e307258dSKrzysztof Helt NULL) < 0) 595e307258dSKrzysztof Helt snd_printk(KERN_ERR "no MPU-401 device at 0x%lx ?\n", 596e307258dSKrzysztof Helt mpu_port[dev]); 597e307258dSKrzysztof Helt } 598e307258dSKrzysztof Helt 599e307258dSKrzysztof Helt strcpy(card->driver, DRV_NAME); 600e307258dSKrzysztof Helt strcpy(card->shortname, "SC-6000"); 601e307258dSKrzysztof Helt sprintf(card->longname, "Gallant SC-6000 at 0x%lx, irq %d, dma %d", 602e307258dSKrzysztof Helt mss_port[dev], xirq, xdma); 603e307258dSKrzysztof Helt 604e307258dSKrzysztof Helt snd_card_set_dev(card, devptr); 605e307258dSKrzysztof Helt 606e307258dSKrzysztof Helt err = snd_card_register(card); 607e307258dSKrzysztof Helt if (err < 0) 608e307258dSKrzysztof Helt goto err_unmap2; 609e307258dSKrzysztof Helt 610e307258dSKrzysztof Helt dev_set_drvdata(devptr, card); 611e307258dSKrzysztof Helt return 0; 612e307258dSKrzysztof Helt 613e307258dSKrzysztof Helt err_unmap2: 614e307258dSKrzysztof Helt release_region(mss_port[dev], 4); 615e307258dSKrzysztof Helt err_unmap1: 616e307258dSKrzysztof Helt release_region(port[dev], 0x10); 617e307258dSKrzysztof Helt err_exit: 618e307258dSKrzysztof Helt snd_card_free(card); 619e307258dSKrzysztof Helt return err; 620e307258dSKrzysztof Helt } 621e307258dSKrzysztof Helt 622e307258dSKrzysztof Helt static int __devexit snd_sc6000_remove(struct device *devptr, unsigned int dev) 623e307258dSKrzysztof Helt { 624e307258dSKrzysztof Helt release_region(port[dev], 0x10); 625e307258dSKrzysztof Helt release_region(mss_port[dev], 4); 626e307258dSKrzysztof Helt 627e307258dSKrzysztof Helt snd_card_free(dev_get_drvdata(devptr)); 628e307258dSKrzysztof Helt dev_set_drvdata(devptr, NULL); 629e307258dSKrzysztof Helt return 0; 630e307258dSKrzysztof Helt } 631e307258dSKrzysztof Helt 632e307258dSKrzysztof Helt static struct isa_driver snd_sc6000_driver = { 633e307258dSKrzysztof Helt .match = snd_sc6000_match, 634e307258dSKrzysztof Helt .probe = snd_sc6000_probe, 635e307258dSKrzysztof Helt .remove = __devexit_p(snd_sc6000_remove), 636e307258dSKrzysztof Helt /* FIXME: suspend/resume */ 637e307258dSKrzysztof Helt .driver = { 638e307258dSKrzysztof Helt .name = DRV_NAME, 639e307258dSKrzysztof Helt }, 640e307258dSKrzysztof Helt }; 641e307258dSKrzysztof Helt 642e307258dSKrzysztof Helt 643e307258dSKrzysztof Helt static int __init alsa_card_sc6000_init(void) 644e307258dSKrzysztof Helt { 645e307258dSKrzysztof Helt return isa_register_driver(&snd_sc6000_driver, SNDRV_CARDS); 646e307258dSKrzysztof Helt } 647e307258dSKrzysztof Helt 648e307258dSKrzysztof Helt static void __exit alsa_card_sc6000_exit(void) 649e307258dSKrzysztof Helt { 650e307258dSKrzysztof Helt isa_unregister_driver(&snd_sc6000_driver); 651e307258dSKrzysztof Helt } 652e307258dSKrzysztof Helt 653e307258dSKrzysztof Helt module_init(alsa_card_sc6000_init) 654e307258dSKrzysztof Helt module_exit(alsa_card_sc6000_exit) 655