diff options
Diffstat (limited to 'sound/isa/sc6000.c')
| -rw-r--r-- | sound/isa/sc6000.c | 134 |
1 files changed, 106 insertions, 28 deletions
diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c index 782010608ef4..9a8bbf6dd62a 100644 --- a/sound/isa/sc6000.c +++ b/sound/isa/sc6000.c | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | * Driver for Gallant SC-6000 soundcard. This card is also known as | 2 | * Driver for Gallant SC-6000 soundcard. This card is also known as |
| 3 | * Audio Excel DSP 16 or Zoltrix AV302. | 3 | * Audio Excel DSP 16 or Zoltrix AV302. |
| 4 | * These cards use CompuMedia ASC-9308 chip + AD1848 codec. | 4 | * These cards use CompuMedia ASC-9308 chip + AD1848 codec. |
| 5 | * SC-6600 and SC-7000 cards are also supported. They are based on | ||
| 6 | * CompuMedia ASC-9408 chip and CS4231 codec. | ||
| 5 | * | 7 | * |
| 6 | * Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl> | 8 | * Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl> |
| 7 | * | 9 | * |
| @@ -54,6 +56,7 @@ static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | |||
| 54 | /* 0x300, 0x310, 0x320, 0x330 */ | 56 | /* 0x300, 0x310, 0x320, 0x330 */ |
| 55 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5, 7, 9, 10, 0 */ | 57 | static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5, 7, 9, 10, 0 */ |
| 56 | static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0, 1, 3 */ | 58 | static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0, 1, 3 */ |
| 59 | static bool joystick[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = false }; | ||
| 57 | 60 | ||
| 58 | module_param_array(index, int, NULL, 0444); | 61 | module_param_array(index, int, NULL, 0444); |
| 59 | MODULE_PARM_DESC(index, "Index value for sc-6000 based soundcard."); | 62 | MODULE_PARM_DESC(index, "Index value for sc-6000 based soundcard."); |
| @@ -73,6 +76,8 @@ module_param_array(mpu_irq, int, NULL, 0444); | |||
| 73 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver."); | 76 | MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver."); |
| 74 | module_param_array(dma, int, NULL, 0444); | 77 | module_param_array(dma, int, NULL, 0444); |
| 75 | MODULE_PARM_DESC(dma, "DMA # for sc-6000 driver."); | 78 | MODULE_PARM_DESC(dma, "DMA # for sc-6000 driver."); |
| 79 | module_param_array(joystick, bool, NULL, 0444); | ||
| 80 | MODULE_PARM_DESC(joystick, "Enable gameport."); | ||
| 76 | 81 | ||
| 77 | /* | 82 | /* |
| 78 | * Commands of SC6000's DSP (SBPRO+special). | 83 | * Commands of SC6000's DSP (SBPRO+special). |
| @@ -191,7 +196,7 @@ static __devinit unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq) | |||
| 191 | return val; | 196 | return val; |
| 192 | } | 197 | } |
| 193 | 198 | ||
| 194 | static __devinit int sc6000_wait_data(char __iomem *vport) | 199 | static int sc6000_wait_data(char __iomem *vport) |
| 195 | { | 200 | { |
| 196 | int loop = 1000; | 201 | int loop = 1000; |
| 197 | unsigned char val = 0; | 202 | unsigned char val = 0; |
| @@ -206,7 +211,7 @@ static __devinit int sc6000_wait_data(char __iomem *vport) | |||
| 206 | return -EAGAIN; | 211 | return -EAGAIN; |
| 207 | } | 212 | } |
| 208 | 213 | ||
| 209 | static __devinit int sc6000_read(char __iomem *vport) | 214 | static int sc6000_read(char __iomem *vport) |
| 210 | { | 215 | { |
| 211 | if (sc6000_wait_data(vport)) | 216 | if (sc6000_wait_data(vport)) |
| 212 | return -EBUSY; | 217 | return -EBUSY; |
| @@ -215,7 +220,7 @@ static __devinit int sc6000_read(char __iomem *vport) | |||
| 215 | 220 | ||
| 216 | } | 221 | } |
| 217 | 222 | ||
| 218 | static __devinit int sc6000_write(char __iomem *vport, int cmd) | 223 | static int sc6000_write(char __iomem *vport, int cmd) |
| 219 | { | 224 | { |
| 220 | unsigned char val; | 225 | unsigned char val; |
| 221 | int loop = 500000; | 226 | int loop = 500000; |
| @@ -276,8 +281,33 @@ static int __devinit sc6000_dsp_reset(char __iomem *vport) | |||
| 276 | } | 281 | } |
| 277 | 282 | ||
| 278 | /* detection and initialization */ | 283 | /* detection and initialization */ |
| 279 | static int __devinit sc6000_cfg_write(char __iomem *vport, | 284 | static int __devinit sc6000_hw_cfg_write(char __iomem *vport, const int *cfg) |
| 280 | unsigned char softcfg) | 285 | { |
| 286 | if (sc6000_write(vport, COMMAND_6C) < 0) { | ||
| 287 | snd_printk(KERN_WARNING "CMD 0x%x: failed!\n", COMMAND_6C); | ||
| 288 | return -EIO; | ||
| 289 | } | ||
| 290 | if (sc6000_write(vport, COMMAND_5C) < 0) { | ||
| 291 | snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_5C); | ||
| 292 | return -EIO; | ||
| 293 | } | ||
| 294 | if (sc6000_write(vport, cfg[0]) < 0) { | ||
| 295 | snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[0]); | ||
| 296 | return -EIO; | ||
| 297 | } | ||
| 298 | if (sc6000_write(vport, cfg[1]) < 0) { | ||
| 299 | snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[1]); | ||
| 300 | return -EIO; | ||
| 301 | } | ||
| 302 | if (sc6000_write(vport, COMMAND_C5) < 0) { | ||
| 303 | snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_C5); | ||
| 304 | return -EIO; | ||
| 305 | } | ||
| 306 | |||
| 307 | return 0; | ||
| 308 | } | ||
| 309 | |||
| 310 | static int sc6000_cfg_write(char __iomem *vport, unsigned char softcfg) | ||
| 281 | { | 311 | { |
| 282 | 312 | ||
| 283 | if (sc6000_write(vport, WRITE_MDIRQ_CFG)) { | 313 | if (sc6000_write(vport, WRITE_MDIRQ_CFG)) { |
| @@ -291,7 +321,7 @@ static int __devinit sc6000_cfg_write(char __iomem *vport, | |||
| 291 | return 0; | 321 | return 0; |
| 292 | } | 322 | } |
| 293 | 323 | ||
| 294 | static int __devinit sc6000_setup_board(char __iomem *vport, int config) | 324 | static int sc6000_setup_board(char __iomem *vport, int config) |
| 295 | { | 325 | { |
| 296 | int loop = 10; | 326 | int loop = 10; |
| 297 | 327 | ||
| @@ -334,16 +364,39 @@ static int __devinit sc6000_init_mss(char __iomem *vport, int config, | |||
| 334 | return 0; | 364 | return 0; |
| 335 | } | 365 | } |
| 336 | 366 | ||
| 337 | static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma, | 367 | static void __devinit sc6000_hw_cfg_encode(char __iomem *vport, int *cfg, |
| 338 | char __iomem *vmss_port, int mpu_irq) | 368 | long xport, long xmpu, |
| 369 | long xmss_port, int joystick) | ||
| 370 | { | ||
| 371 | cfg[0] = 0; | ||
| 372 | cfg[1] = 0; | ||
| 373 | if (xport == 0x240) | ||
| 374 | cfg[0] |= 1; | ||
| 375 | if (xmpu != SNDRV_AUTO_PORT) { | ||
| 376 | cfg[0] |= (xmpu & 0x30) >> 2; | ||
| 377 | cfg[1] |= 0x20; | ||
| 378 | } | ||
| 379 | if (xmss_port == 0xe80) | ||
| 380 | cfg[0] |= 0x10; | ||
| 381 | cfg[0] |= 0x40; /* always set */ | ||
| 382 | if (!joystick) | ||
| 383 | cfg[0] |= 0x02; | ||
| 384 | cfg[1] |= 0x80; /* enable WSS system */ | ||
| 385 | cfg[1] &= ~0x40; /* disable IDE */ | ||
| 386 | snd_printd("hw cfg %x, %x\n", cfg[0], cfg[1]); | ||
| 387 | } | ||
| 388 | |||
| 389 | static int __devinit sc6000_init_board(char __iomem *vport, | ||
| 390 | char __iomem *vmss_port, int dev) | ||
| 339 | { | 391 | { |
| 340 | char answer[15]; | 392 | char answer[15]; |
| 341 | char version[2]; | 393 | char version[2]; |
| 342 | int mss_config = sc6000_irq_to_softcfg(irq) | | 394 | int mss_config = sc6000_irq_to_softcfg(irq[dev]) | |
| 343 | sc6000_dma_to_softcfg(dma); | 395 | sc6000_dma_to_softcfg(dma[dev]); |
| 344 | int config = mss_config | | 396 | int config = mss_config | |
| 345 | sc6000_mpu_irq_to_softcfg(mpu_irq); | 397 | sc6000_mpu_irq_to_softcfg(mpu_irq[dev]); |
| 346 | int err; | 398 | int err; |
| 399 | int old = 0; | ||
| 347 | 400 | ||
| 348 | err = sc6000_dsp_reset(vport); | 401 | err = sc6000_dsp_reset(vport); |
| 349 | if (err < 0) { | 402 | if (err < 0) { |
| @@ -360,7 +413,6 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma, | |||
| 360 | /* | 413 | /* |
| 361 | * My SC-6000 card return "SC-6000" in DSPCopyright, so | 414 | * My SC-6000 card return "SC-6000" in DSPCopyright, so |
| 362 | * if we have something different, we have to be warned. | 415 | * if we have something different, we have to be warned. |
| 363 | * Mine returns "SC-6000A " - KH | ||
| 364 | */ | 416 | */ |
| 365 | if (strncmp("SC-6000", answer, 7)) | 417 | if (strncmp("SC-6000", answer, 7)) |
| 366 | snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n"); | 418 | snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n"); |
| @@ -372,13 +424,32 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma, | |||
| 372 | printk(KERN_INFO PFX "Detected model: %s, DSP version %d.%d\n", | 424 | printk(KERN_INFO PFX "Detected model: %s, DSP version %d.%d\n", |
| 373 | answer, version[0], version[1]); | 425 | answer, version[0], version[1]); |
| 374 | 426 | ||
| 375 | /* | 427 | /* set configuration */ |
| 376 | * 0x0A == (IRQ 7, DMA 1, MIRQ 0) | 428 | sc6000_write(vport, COMMAND_5C); |
| 377 | */ | 429 | if (sc6000_read(vport) < 0) |
| 378 | err = sc6000_cfg_write(vport, 0x0a); | 430 | old = 1; |
| 431 | |||
| 432 | if (!old) { | ||
| 433 | int cfg[2]; | ||
| 434 | sc6000_hw_cfg_encode(vport, &cfg[0], port[dev], mpu_port[dev], | ||
| 435 | mss_port[dev], joystick[dev]); | ||
| 436 | if (sc6000_hw_cfg_write(vport, cfg) < 0) { | ||
| 437 | snd_printk(KERN_ERR "sc6000_hw_cfg_write: failed!\n"); | ||
| 438 | return -EIO; | ||
| 439 | } | ||
| 440 | } | ||
| 441 | err = sc6000_setup_board(vport, config); | ||
| 379 | if (err < 0) { | 442 | if (err < 0) { |
| 380 | snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n"); | 443 | snd_printk(KERN_ERR "sc6000_setup_board: failed!\n"); |
| 381 | return -EFAULT; | 444 | return -ENODEV; |
| 445 | } | ||
| 446 | |||
| 447 | sc6000_dsp_reset(vport); | ||
| 448 | |||
| 449 | if (!old) { | ||
| 450 | sc6000_write(vport, COMMAND_60); | ||
| 451 | sc6000_write(vport, 0x02); | ||
| 452 | sc6000_dsp_reset(vport); | ||
| 382 | } | 453 | } |
| 383 | 454 | ||
| 384 | err = sc6000_setup_board(vport, config); | 455 | err = sc6000_setup_board(vport, config); |
| @@ -386,10 +457,9 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma, | |||
| 386 | snd_printk(KERN_ERR "sc6000_setup_board: failed!\n"); | 457 | snd_printk(KERN_ERR "sc6000_setup_board: failed!\n"); |
| 387 | return -ENODEV; | 458 | return -ENODEV; |
| 388 | } | 459 | } |
| 389 | |||
| 390 | err = sc6000_init_mss(vport, config, vmss_port, mss_config); | 460 | err = sc6000_init_mss(vport, config, vmss_port, mss_config); |
| 391 | if (err < 0) { | 461 | if (err < 0) { |
| 392 | snd_printk(KERN_ERR "Can not initialize " | 462 | snd_printk(KERN_ERR "Cannot initialize " |
| 393 | "Microsoft Sound System mode.\n"); | 463 | "Microsoft Sound System mode.\n"); |
| 394 | return -ENODEV; | 464 | return -ENODEV; |
| 395 | } | 465 | } |
| @@ -485,14 +555,16 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev) | |||
| 485 | struct snd_card *card; | 555 | struct snd_card *card; |
| 486 | struct snd_wss *chip; | 556 | struct snd_wss *chip; |
| 487 | struct snd_opl3 *opl3; | 557 | struct snd_opl3 *opl3; |
| 488 | char __iomem *vport; | 558 | char __iomem **vport; |
| 489 | char __iomem *vmss_port; | 559 | char __iomem *vmss_port; |
| 490 | 560 | ||
| 491 | 561 | ||
| 492 | err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); | 562 | err = snd_card_create(index[dev], id[dev], THIS_MODULE, sizeof(vport), |
| 563 | &card); | ||
| 493 | if (err < 0) | 564 | if (err < 0) |
| 494 | return err; | 565 | return err; |
| 495 | 566 | ||
| 567 | vport = card->private_data; | ||
| 496 | if (xirq == SNDRV_AUTO_IRQ) { | 568 | if (xirq == SNDRV_AUTO_IRQ) { |
| 497 | xirq = snd_legacy_find_free_irq(possible_irqs); | 569 | xirq = snd_legacy_find_free_irq(possible_irqs); |
| 498 | if (xirq < 0) { | 570 | if (xirq < 0) { |
| @@ -517,8 +589,8 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev) | |||
| 517 | err = -EBUSY; | 589 | err = -EBUSY; |
| 518 | goto err_exit; | 590 | goto err_exit; |
| 519 | } | 591 | } |
| 520 | vport = devm_ioport_map(devptr, port[dev], 0x10); | 592 | *vport = devm_ioport_map(devptr, port[dev], 0x10); |
| 521 | if (!vport) { | 593 | if (*vport == NULL) { |
| 522 | snd_printk(KERN_ERR PFX | 594 | snd_printk(KERN_ERR PFX |
| 523 | "I/O port cannot be iomaped.\n"); | 595 | "I/O port cannot be iomaped.\n"); |
| 524 | err = -EBUSY; | 596 | err = -EBUSY; |
| @@ -533,7 +605,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev) | |||
| 533 | goto err_unmap1; | 605 | goto err_unmap1; |
| 534 | } | 606 | } |
| 535 | vmss_port = devm_ioport_map(devptr, mss_port[dev], 4); | 607 | vmss_port = devm_ioport_map(devptr, mss_port[dev], 4); |
| 536 | if (!vport) { | 608 | if (!vmss_port) { |
| 537 | snd_printk(KERN_ERR PFX | 609 | snd_printk(KERN_ERR PFX |
| 538 | "MSS port I/O cannot be iomaped.\n"); | 610 | "MSS port I/O cannot be iomaped.\n"); |
| 539 | err = -EBUSY; | 611 | err = -EBUSY; |
| @@ -544,7 +616,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev) | |||
| 544 | port[dev], xirq, xdma, | 616 | port[dev], xirq, xdma, |
| 545 | mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]); | 617 | mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]); |
| 546 | 618 | ||
| 547 | err = sc6000_init_board(vport, xirq, xdma, vmss_port, mpu_irq[dev]); | 619 | err = sc6000_init_board(*vport, vmss_port, dev); |
| 548 | if (err < 0) | 620 | if (err < 0) |
| 549 | goto err_unmap2; | 621 | goto err_unmap2; |
| 550 | 622 | ||
| @@ -552,7 +624,6 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev) | |||
| 552 | WSS_HW_DETECT, 0, &chip); | 624 | WSS_HW_DETECT, 0, &chip); |
| 553 | if (err < 0) | 625 | if (err < 0) |
| 554 | goto err_unmap2; | 626 | goto err_unmap2; |
| 555 | card->private_data = chip; | ||
| 556 | 627 | ||
| 557 | err = snd_wss_pcm(chip, 0, NULL); | 628 | err = snd_wss_pcm(chip, 0, NULL); |
| 558 | if (err < 0) { | 629 | if (err < 0) { |
| @@ -608,6 +679,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev) | |||
| 608 | return 0; | 679 | return 0; |
| 609 | 680 | ||
| 610 | err_unmap2: | 681 | err_unmap2: |
| 682 | sc6000_setup_board(*vport, 0); | ||
| 611 | release_region(mss_port[dev], 4); | 683 | release_region(mss_port[dev], 4); |
| 612 | err_unmap1: | 684 | err_unmap1: |
| 613 | release_region(port[dev], 0x10); | 685 | release_region(port[dev], 0x10); |
| @@ -618,11 +690,17 @@ err_exit: | |||
| 618 | 690 | ||
| 619 | static int __devexit snd_sc6000_remove(struct device *devptr, unsigned int dev) | 691 | static int __devexit snd_sc6000_remove(struct device *devptr, unsigned int dev) |
| 620 | { | 692 | { |
| 693 | struct snd_card *card = dev_get_drvdata(devptr); | ||
| 694 | char __iomem **vport = card->private_data; | ||
| 695 | |||
| 696 | if (sc6000_setup_board(*vport, 0) < 0) | ||
| 697 | snd_printk(KERN_WARNING "sc6000_setup_board failed on exit!\n"); | ||
| 698 | |||
| 621 | release_region(port[dev], 0x10); | 699 | release_region(port[dev], 0x10); |
| 622 | release_region(mss_port[dev], 4); | 700 | release_region(mss_port[dev], 4); |
| 623 | 701 | ||
| 624 | snd_card_free(dev_get_drvdata(devptr)); | ||
| 625 | dev_set_drvdata(devptr, NULL); | 702 | dev_set_drvdata(devptr, NULL); |
| 703 | snd_card_free(card); | ||
| 626 | return 0; | 704 | return 0; |
| 627 | } | 705 | } |
| 628 | 706 | ||
