diff options
Diffstat (limited to 'sound/isa/sc6000.c')
-rw-r--r-- | sound/isa/sc6000.c | 127 |
1 files changed, 99 insertions, 28 deletions
diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c index 782010608ef4..983ab7e3b5b4 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 | * |
@@ -191,7 +193,7 @@ static __devinit unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq) | |||
191 | return val; | 193 | return val; |
192 | } | 194 | } |
193 | 195 | ||
194 | static __devinit int sc6000_wait_data(char __iomem *vport) | 196 | static int sc6000_wait_data(char __iomem *vport) |
195 | { | 197 | { |
196 | int loop = 1000; | 198 | int loop = 1000; |
197 | unsigned char val = 0; | 199 | unsigned char val = 0; |
@@ -206,7 +208,7 @@ static __devinit int sc6000_wait_data(char __iomem *vport) | |||
206 | return -EAGAIN; | 208 | return -EAGAIN; |
207 | } | 209 | } |
208 | 210 | ||
209 | static __devinit int sc6000_read(char __iomem *vport) | 211 | static int sc6000_read(char __iomem *vport) |
210 | { | 212 | { |
211 | if (sc6000_wait_data(vport)) | 213 | if (sc6000_wait_data(vport)) |
212 | return -EBUSY; | 214 | return -EBUSY; |
@@ -215,7 +217,7 @@ static __devinit int sc6000_read(char __iomem *vport) | |||
215 | 217 | ||
216 | } | 218 | } |
217 | 219 | ||
218 | static __devinit int sc6000_write(char __iomem *vport, int cmd) | 220 | static int sc6000_write(char __iomem *vport, int cmd) |
219 | { | 221 | { |
220 | unsigned char val; | 222 | unsigned char val; |
221 | int loop = 500000; | 223 | int loop = 500000; |
@@ -276,8 +278,33 @@ static int __devinit sc6000_dsp_reset(char __iomem *vport) | |||
276 | } | 278 | } |
277 | 279 | ||
278 | /* detection and initialization */ | 280 | /* detection and initialization */ |
279 | static int __devinit sc6000_cfg_write(char __iomem *vport, | 281 | static int __devinit sc6000_hw_cfg_write(char __iomem *vport, const int *cfg) |
280 | unsigned char softcfg) | 282 | { |
283 | if (sc6000_write(vport, COMMAND_6C) < 0) { | ||
284 | snd_printk(KERN_WARNING "CMD 0x%x: failed!\n", COMMAND_6C); | ||
285 | return -EIO; | ||
286 | } | ||
287 | if (sc6000_write(vport, COMMAND_5C) < 0) { | ||
288 | snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_5C); | ||
289 | return -EIO; | ||
290 | } | ||
291 | if (sc6000_write(vport, cfg[0]) < 0) { | ||
292 | snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[0]); | ||
293 | return -EIO; | ||
294 | } | ||
295 | if (sc6000_write(vport, cfg[1]) < 0) { | ||
296 | snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[1]); | ||
297 | return -EIO; | ||
298 | } | ||
299 | if (sc6000_write(vport, COMMAND_C5) < 0) { | ||
300 | snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_C5); | ||
301 | return -EIO; | ||
302 | } | ||
303 | |||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | static int sc6000_cfg_write(char __iomem *vport, unsigned char softcfg) | ||
281 | { | 308 | { |
282 | 309 | ||
283 | if (sc6000_write(vport, WRITE_MDIRQ_CFG)) { | 310 | if (sc6000_write(vport, WRITE_MDIRQ_CFG)) { |
@@ -291,7 +318,7 @@ static int __devinit sc6000_cfg_write(char __iomem *vport, | |||
291 | return 0; | 318 | return 0; |
292 | } | 319 | } |
293 | 320 | ||
294 | static int __devinit sc6000_setup_board(char __iomem *vport, int config) | 321 | static int sc6000_setup_board(char __iomem *vport, int config) |
295 | { | 322 | { |
296 | int loop = 10; | 323 | int loop = 10; |
297 | 324 | ||
@@ -334,16 +361,38 @@ static int __devinit sc6000_init_mss(char __iomem *vport, int config, | |||
334 | return 0; | 361 | return 0; |
335 | } | 362 | } |
336 | 363 | ||
337 | static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma, | 364 | static void __devinit sc6000_hw_cfg_encode(char __iomem *vport, int *cfg, |
338 | char __iomem *vmss_port, int mpu_irq) | 365 | long xport, long xmpu, |
366 | long xmss_port) | ||
367 | { | ||
368 | cfg[0] = 0; | ||
369 | cfg[1] = 0; | ||
370 | if (xport == 0x240) | ||
371 | cfg[0] |= 1; | ||
372 | if (xmpu != SNDRV_AUTO_PORT) { | ||
373 | cfg[0] |= (xmpu & 0x30) >> 2; | ||
374 | cfg[1] |= 0x20; | ||
375 | } | ||
376 | if (xmss_port == 0xe80) | ||
377 | cfg[0] |= 0x10; | ||
378 | cfg[0] |= 0x40; /* always set */ | ||
379 | cfg[1] |= 0x80; /* enable WSS system */ | ||
380 | cfg[1] &= ~0x40; /* disable IDE */ | ||
381 | snd_printd("hw cfg %x, %x\n", cfg[0], cfg[1]); | ||
382 | } | ||
383 | |||
384 | static int __devinit sc6000_init_board(char __iomem *vport, | ||
385 | char __iomem *vmss_port, int dev) | ||
339 | { | 386 | { |
340 | char answer[15]; | 387 | char answer[15]; |
341 | char version[2]; | 388 | char version[2]; |
342 | int mss_config = sc6000_irq_to_softcfg(irq) | | 389 | int mss_config = sc6000_irq_to_softcfg(irq[dev]) | |
343 | sc6000_dma_to_softcfg(dma); | 390 | sc6000_dma_to_softcfg(dma[dev]); |
344 | int config = mss_config | | 391 | int config = mss_config | |
345 | sc6000_mpu_irq_to_softcfg(mpu_irq); | 392 | sc6000_mpu_irq_to_softcfg(mpu_irq[dev]); |
346 | int err; | 393 | int err; |
394 | int cfg[2]; | ||
395 | int old = 0; | ||
347 | 396 | ||
348 | err = sc6000_dsp_reset(vport); | 397 | err = sc6000_dsp_reset(vport); |
349 | if (err < 0) { | 398 | if (err < 0) { |
@@ -360,7 +409,6 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma, | |||
360 | /* | 409 | /* |
361 | * My SC-6000 card return "SC-6000" in DSPCopyright, so | 410 | * My SC-6000 card return "SC-6000" in DSPCopyright, so |
362 | * if we have something different, we have to be warned. | 411 | * if we have something different, we have to be warned. |
363 | * Mine returns "SC-6000A " - KH | ||
364 | */ | 412 | */ |
365 | if (strncmp("SC-6000", answer, 7)) | 413 | if (strncmp("SC-6000", answer, 7)) |
366 | snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n"); | 414 | snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n"); |
@@ -372,13 +420,29 @@ 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", | 420 | printk(KERN_INFO PFX "Detected model: %s, DSP version %d.%d\n", |
373 | answer, version[0], version[1]); | 421 | answer, version[0], version[1]); |
374 | 422 | ||
375 | /* | 423 | /* set configuration */ |
376 | * 0x0A == (IRQ 7, DMA 1, MIRQ 0) | 424 | sc6000_hw_cfg_encode(vport, &cfg[0], port[dev], mpu_port[dev], |
377 | */ | 425 | mss_port[dev]); |
378 | err = sc6000_cfg_write(vport, 0x0a); | 426 | if (sc6000_hw_cfg_write(vport, cfg) < 0) { |
427 | snd_printk(KERN_ERR "sc6000_hw_cfg_write: failed!\n"); | ||
428 | return -EIO; | ||
429 | } | ||
430 | err = sc6000_setup_board(vport, config); | ||
379 | if (err < 0) { | 431 | if (err < 0) { |
380 | snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n"); | 432 | snd_printk(KERN_ERR "sc6000_setup_board: failed!\n"); |
381 | return -EFAULT; | 433 | return -ENODEV; |
434 | } | ||
435 | |||
436 | sc6000_dsp_reset(vport); | ||
437 | sc6000_write(vport, COMMAND_5C); | ||
438 | if (sc6000_read(vport) < 0) | ||
439 | old = 1; | ||
440 | sc6000_dsp_reset(vport); | ||
441 | |||
442 | if (!old) { | ||
443 | sc6000_write(vport, COMMAND_60); | ||
444 | sc6000_write(vport, 0x02); | ||
445 | sc6000_dsp_reset(vport); | ||
382 | } | 446 | } |
383 | 447 | ||
384 | err = sc6000_setup_board(vport, config); | 448 | err = sc6000_setup_board(vport, config); |
@@ -386,10 +450,9 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma, | |||
386 | snd_printk(KERN_ERR "sc6000_setup_board: failed!\n"); | 450 | snd_printk(KERN_ERR "sc6000_setup_board: failed!\n"); |
387 | return -ENODEV; | 451 | return -ENODEV; |
388 | } | 452 | } |
389 | |||
390 | err = sc6000_init_mss(vport, config, vmss_port, mss_config); | 453 | err = sc6000_init_mss(vport, config, vmss_port, mss_config); |
391 | if (err < 0) { | 454 | if (err < 0) { |
392 | snd_printk(KERN_ERR "Can not initialize " | 455 | snd_printk(KERN_ERR "Cannot initialize " |
393 | "Microsoft Sound System mode.\n"); | 456 | "Microsoft Sound System mode.\n"); |
394 | return -ENODEV; | 457 | return -ENODEV; |
395 | } | 458 | } |
@@ -485,14 +548,16 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev) | |||
485 | struct snd_card *card; | 548 | struct snd_card *card; |
486 | struct snd_wss *chip; | 549 | struct snd_wss *chip; |
487 | struct snd_opl3 *opl3; | 550 | struct snd_opl3 *opl3; |
488 | char __iomem *vport; | 551 | char __iomem **vport; |
489 | char __iomem *vmss_port; | 552 | char __iomem *vmss_port; |
490 | 553 | ||
491 | 554 | ||
492 | err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); | 555 | err = snd_card_create(index[dev], id[dev], THIS_MODULE, sizeof(vport), |
556 | &card); | ||
493 | if (err < 0) | 557 | if (err < 0) |
494 | return err; | 558 | return err; |
495 | 559 | ||
560 | vport = card->private_data; | ||
496 | if (xirq == SNDRV_AUTO_IRQ) { | 561 | if (xirq == SNDRV_AUTO_IRQ) { |
497 | xirq = snd_legacy_find_free_irq(possible_irqs); | 562 | xirq = snd_legacy_find_free_irq(possible_irqs); |
498 | if (xirq < 0) { | 563 | if (xirq < 0) { |
@@ -517,8 +582,8 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev) | |||
517 | err = -EBUSY; | 582 | err = -EBUSY; |
518 | goto err_exit; | 583 | goto err_exit; |
519 | } | 584 | } |
520 | vport = devm_ioport_map(devptr, port[dev], 0x10); | 585 | *vport = devm_ioport_map(devptr, port[dev], 0x10); |
521 | if (!vport) { | 586 | if (*vport == NULL) { |
522 | snd_printk(KERN_ERR PFX | 587 | snd_printk(KERN_ERR PFX |
523 | "I/O port cannot be iomaped.\n"); | 588 | "I/O port cannot be iomaped.\n"); |
524 | err = -EBUSY; | 589 | err = -EBUSY; |
@@ -533,7 +598,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev) | |||
533 | goto err_unmap1; | 598 | goto err_unmap1; |
534 | } | 599 | } |
535 | vmss_port = devm_ioport_map(devptr, mss_port[dev], 4); | 600 | vmss_port = devm_ioport_map(devptr, mss_port[dev], 4); |
536 | if (!vport) { | 601 | if (!vmss_port) { |
537 | snd_printk(KERN_ERR PFX | 602 | snd_printk(KERN_ERR PFX |
538 | "MSS port I/O cannot be iomaped.\n"); | 603 | "MSS port I/O cannot be iomaped.\n"); |
539 | err = -EBUSY; | 604 | err = -EBUSY; |
@@ -544,7 +609,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev) | |||
544 | port[dev], xirq, xdma, | 609 | port[dev], xirq, xdma, |
545 | mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]); | 610 | mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]); |
546 | 611 | ||
547 | err = sc6000_init_board(vport, xirq, xdma, vmss_port, mpu_irq[dev]); | 612 | err = sc6000_init_board(*vport, vmss_port, dev); |
548 | if (err < 0) | 613 | if (err < 0) |
549 | goto err_unmap2; | 614 | goto err_unmap2; |
550 | 615 | ||
@@ -552,7 +617,6 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev) | |||
552 | WSS_HW_DETECT, 0, &chip); | 617 | WSS_HW_DETECT, 0, &chip); |
553 | if (err < 0) | 618 | if (err < 0) |
554 | goto err_unmap2; | 619 | goto err_unmap2; |
555 | card->private_data = chip; | ||
556 | 620 | ||
557 | err = snd_wss_pcm(chip, 0, NULL); | 621 | err = snd_wss_pcm(chip, 0, NULL); |
558 | if (err < 0) { | 622 | if (err < 0) { |
@@ -608,6 +672,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev) | |||
608 | return 0; | 672 | return 0; |
609 | 673 | ||
610 | err_unmap2: | 674 | err_unmap2: |
675 | sc6000_setup_board(*vport, 0); | ||
611 | release_region(mss_port[dev], 4); | 676 | release_region(mss_port[dev], 4); |
612 | err_unmap1: | 677 | err_unmap1: |
613 | release_region(port[dev], 0x10); | 678 | release_region(port[dev], 0x10); |
@@ -618,11 +683,17 @@ err_exit: | |||
618 | 683 | ||
619 | static int __devexit snd_sc6000_remove(struct device *devptr, unsigned int dev) | 684 | static int __devexit snd_sc6000_remove(struct device *devptr, unsigned int dev) |
620 | { | 685 | { |
686 | struct snd_card *card = dev_get_drvdata(devptr); | ||
687 | char __iomem **vport = card->private_data; | ||
688 | |||
689 | if (sc6000_setup_board(*vport, 0) < 0) | ||
690 | snd_printk(KERN_WARNING "sc6000_setup_board failed on exit!\n"); | ||
691 | |||
621 | release_region(port[dev], 0x10); | 692 | release_region(port[dev], 0x10); |
622 | release_region(mss_port[dev], 4); | 693 | release_region(mss_port[dev], 4); |
623 | 694 | ||
624 | snd_card_free(dev_get_drvdata(devptr)); | ||
625 | dev_set_drvdata(devptr, NULL); | 695 | dev_set_drvdata(devptr, NULL); |
696 | snd_card_free(card); | ||
626 | return 0; | 697 | return 0; |
627 | } | 698 | } |
628 | 699 | ||