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