diff options
Diffstat (limited to 'sound/isa/cmi8330.c')
-rw-r--r-- | sound/isa/cmi8330.c | 68 |
1 files changed, 60 insertions, 8 deletions
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c index 24e60902f8ca..de83608719ea 100644 --- a/sound/isa/cmi8330.c +++ b/sound/isa/cmi8330.c | |||
@@ -31,11 +31,11 @@ | |||
31 | * To quickly load the module, | 31 | * To quickly load the module, |
32 | * | 32 | * |
33 | * modprobe -a snd-cmi8330 sbport=0x220 sbirq=5 sbdma8=1 | 33 | * modprobe -a snd-cmi8330 sbport=0x220 sbirq=5 sbdma8=1 |
34 | * sbdma16=5 wssport=0x530 wssirq=11 wssdma=0 | 34 | * sbdma16=5 wssport=0x530 wssirq=11 wssdma=0 fmport=0x388 |
35 | * | 35 | * |
36 | * This card has two mixers and two PCM devices. I've cheesed it such | 36 | * This card has two mixers and two PCM devices. I've cheesed it such |
37 | * that recording and playback can be done through the same device. | 37 | * that recording and playback can be done through the same device. |
38 | * The driver "magically" routes the capturing to the AD1848 codec, | 38 | * The driver "magically" routes the capturing to the CMI8330 codec, |
39 | * and playback to the SB16 codec. This allows for full-duplex mode | 39 | * and playback to the SB16 codec. This allows for full-duplex mode |
40 | * to some extent. | 40 | * to some extent. |
41 | * The utilities in alsa-utils are aware of both devices, so passing | 41 | * The utilities in alsa-utils are aware of both devices, so passing |
@@ -51,6 +51,8 @@ | |||
51 | #include <linux/moduleparam.h> | 51 | #include <linux/moduleparam.h> |
52 | #include <sound/core.h> | 52 | #include <sound/core.h> |
53 | #include <sound/wss.h> | 53 | #include <sound/wss.h> |
54 | #include <sound/opl3.h> | ||
55 | #include <sound/mpu401.h> | ||
54 | #include <sound/sb.h> | 56 | #include <sound/sb.h> |
55 | #include <sound/initval.h> | 57 | #include <sound/initval.h> |
56 | 58 | ||
@@ -79,6 +81,9 @@ static int sbdma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; | |||
79 | static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | 81 | static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; |
80 | static int wssirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; | 82 | static int wssirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; |
81 | static int wssdma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; | 83 | static int wssdma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; |
84 | static long fmport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | ||
85 | static long mpuport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; | ||
86 | static int mpuirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; | ||
82 | 87 | ||
83 | module_param_array(index, int, NULL, 0444); | 88 | module_param_array(index, int, NULL, 0444); |
84 | MODULE_PARM_DESC(index, "Index value for CMI8330 soundcard."); | 89 | MODULE_PARM_DESC(index, "Index value for CMI8330 soundcard."); |
@@ -107,6 +112,12 @@ MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330 WSS driver."); | |||
107 | module_param_array(wssdma, int, NULL, 0444); | 112 | module_param_array(wssdma, int, NULL, 0444); |
108 | MODULE_PARM_DESC(wssdma, "DMA for CMI8330 WSS driver."); | 113 | MODULE_PARM_DESC(wssdma, "DMA for CMI8330 WSS driver."); |
109 | 114 | ||
115 | module_param_array(fmport, long, NULL, 0444); | ||
116 | MODULE_PARM_DESC(fmport, "FM port # for CMI8330 driver."); | ||
117 | module_param_array(mpuport, long, NULL, 0444); | ||
118 | MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8330 driver."); | ||
119 | module_param_array(mpuirq, int, NULL, 0444); | ||
120 | MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8330 MPU-401 port."); | ||
110 | #ifdef CONFIG_PNP | 121 | #ifdef CONFIG_PNP |
111 | static int isa_registered; | 122 | static int isa_registered; |
112 | static int pnp_registered; | 123 | static int pnp_registered; |
@@ -149,6 +160,7 @@ struct snd_cmi8330 { | |||
149 | #ifdef CONFIG_PNP | 160 | #ifdef CONFIG_PNP |
150 | struct pnp_dev *cap; | 161 | struct pnp_dev *cap; |
151 | struct pnp_dev *play; | 162 | struct pnp_dev *play; |
163 | struct pnp_dev *mpu; | ||
152 | #endif | 164 | #endif |
153 | struct snd_card *card; | 165 | struct snd_card *card; |
154 | struct snd_wss *wss; | 166 | struct snd_wss *wss; |
@@ -165,7 +177,7 @@ struct snd_cmi8330 { | |||
165 | #ifdef CONFIG_PNP | 177 | #ifdef CONFIG_PNP |
166 | 178 | ||
167 | static struct pnp_card_device_id snd_cmi8330_pnpids[] = { | 179 | static struct pnp_card_device_id snd_cmi8330_pnpids[] = { |
168 | { .id = "CMI0001", .devs = { { "@@@0001" }, { "@X@0001" } } }, | 180 | { .id = "CMI0001", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } } }, |
169 | { .id = "" } | 181 | { .id = "" } |
170 | }; | 182 | }; |
171 | 183 | ||
@@ -219,8 +231,10 @@ WSS_SINGLE("3D Control - Switch", 0, | |||
219 | CMI8330_RMUX3D, 5, 1, 1), | 231 | CMI8330_RMUX3D, 5, 1, 1), |
220 | WSS_SINGLE("PC Speaker Playback Volume", 0, | 232 | WSS_SINGLE("PC Speaker Playback Volume", 0, |
221 | CMI8330_OUTPUTVOL, 3, 3, 0), | 233 | CMI8330_OUTPUTVOL, 3, 3, 0), |
222 | WSS_SINGLE("FM Playback Switch", 0, | 234 | WSS_DOUBLE("FM Playback Switch", 0, |
223 | CMI8330_RECMUX, 3, 1, 1), | 235 | CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), |
236 | WSS_DOUBLE("FM Playback Volume", 0, | ||
237 | CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), | ||
224 | WSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", CAPTURE, SWITCH), 0, | 238 | WSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", CAPTURE, SWITCH), 0, |
225 | CMI8330_RMUX3D, 7, 1, 1), | 239 | CMI8330_RMUX3D, 7, 1, 1), |
226 | WSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", PLAYBACK, SWITCH), 0, | 240 | WSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", PLAYBACK, SWITCH), 0, |
@@ -323,16 +337,21 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard, | |||
323 | if (acard->play == NULL) | 337 | if (acard->play == NULL) |
324 | return -EBUSY; | 338 | return -EBUSY; |
325 | 339 | ||
340 | acard->mpu = pnp_request_card_device(card, id->devs[2].id, NULL); | ||
341 | if (acard->play == NULL) | ||
342 | return -EBUSY; | ||
343 | |||
326 | pdev = acard->cap; | 344 | pdev = acard->cap; |
327 | 345 | ||
328 | err = pnp_activate_dev(pdev); | 346 | err = pnp_activate_dev(pdev); |
329 | if (err < 0) { | 347 | if (err < 0) { |
330 | snd_printk(KERN_ERR "CMI8330/C3D (AD1848) PnP configure failure\n"); | 348 | snd_printk(KERN_ERR "CMI8330/C3D PnP configure failure\n"); |
331 | return -EBUSY; | 349 | return -EBUSY; |
332 | } | 350 | } |
333 | wssport[dev] = pnp_port_start(pdev, 0); | 351 | wssport[dev] = pnp_port_start(pdev, 0); |
334 | wssdma[dev] = pnp_dma(pdev, 0); | 352 | wssdma[dev] = pnp_dma(pdev, 0); |
335 | wssirq[dev] = pnp_irq(pdev, 0); | 353 | wssirq[dev] = pnp_irq(pdev, 0); |
354 | fmport[dev] = pnp_port_start(pdev, 1); | ||
336 | 355 | ||
337 | /* allocate SB16 resources */ | 356 | /* allocate SB16 resources */ |
338 | pdev = acard->play; | 357 | pdev = acard->play; |
@@ -347,6 +366,17 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard, | |||
347 | sbdma16[dev] = pnp_dma(pdev, 1); | 366 | sbdma16[dev] = pnp_dma(pdev, 1); |
348 | sbirq[dev] = pnp_irq(pdev, 0); | 367 | sbirq[dev] = pnp_irq(pdev, 0); |
349 | 368 | ||
369 | /* allocate MPU-401 resources */ | ||
370 | pdev = acard->mpu; | ||
371 | |||
372 | err = pnp_activate_dev(pdev); | ||
373 | if (err < 0) { | ||
374 | snd_printk(KERN_ERR | ||
375 | "CMI8330/C3D (MPU-401) PnP configure failure\n"); | ||
376 | return -EBUSY; | ||
377 | } | ||
378 | mpuport[dev] = pnp_port_start(pdev, 0); | ||
379 | mpuirq[dev] = pnp_irq(pdev, 0); | ||
350 | return 0; | 380 | return 0; |
351 | } | 381 | } |
352 | #endif | 382 | #endif |
@@ -489,6 +519,7 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev) | |||
489 | { | 519 | { |
490 | struct snd_cmi8330 *acard; | 520 | struct snd_cmi8330 *acard; |
491 | int i, err; | 521 | int i, err; |
522 | struct snd_opl3 *opl3; | ||
492 | 523 | ||
493 | acard = card->private_data; | 524 | acard = card->private_data; |
494 | err = snd_wss_create(card, wssport[dev] + 4, -1, | 525 | err = snd_wss_create(card, wssport[dev] + 4, -1, |
@@ -496,11 +527,11 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev) | |||
496 | wssdma[dev], -1, | 527 | wssdma[dev], -1, |
497 | WSS_HW_DETECT, 0, &acard->wss); | 528 | WSS_HW_DETECT, 0, &acard->wss); |
498 | if (err < 0) { | 529 | if (err < 0) { |
499 | snd_printk(KERN_ERR PFX "(AD1848) device busy??\n"); | 530 | snd_printk(KERN_ERR PFX "(CMI8330) device busy??\n"); |
500 | return err; | 531 | return err; |
501 | } | 532 | } |
502 | if (acard->wss->hardware != WSS_HW_CMI8330) { | 533 | if (acard->wss->hardware != WSS_HW_CMI8330) { |
503 | snd_printk(KERN_ERR PFX "(AD1848) not found during probe\n"); | 534 | snd_printk(KERN_ERR PFX "(CMI8330) not found during probe\n"); |
504 | return -ENODEV; | 535 | return -ENODEV; |
505 | } | 536 | } |
506 | 537 | ||
@@ -532,6 +563,27 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev) | |||
532 | snd_printk(KERN_ERR PFX "failed to create pcms\n"); | 563 | snd_printk(KERN_ERR PFX "failed to create pcms\n"); |
533 | return err; | 564 | return err; |
534 | } | 565 | } |
566 | if (fmport[dev] != SNDRV_AUTO_PORT) { | ||
567 | if (snd_opl3_create(card, | ||
568 | fmport[dev], fmport[dev] + 2, | ||
569 | OPL3_HW_AUTO, 0, &opl3) < 0) { | ||
570 | snd_printk(KERN_ERR PFX | ||
571 | "no OPL device at 0x%lx-0x%lx ?\n", | ||
572 | fmport[dev], fmport[dev] + 2); | ||
573 | } else { | ||
574 | err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); | ||
575 | if (err < 0) | ||
576 | return err; | ||
577 | } | ||
578 | } | ||
579 | |||
580 | if (mpuport[dev] != SNDRV_AUTO_PORT) { | ||
581 | if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, | ||
582 | mpuport[dev], 0, mpuirq[dev], | ||
583 | IRQF_DISABLED, NULL) < 0) | ||
584 | printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", | ||
585 | mpuport[dev]); | ||
586 | } | ||
535 | 587 | ||
536 | strcpy(card->driver, "CMI8330/C3D"); | 588 | strcpy(card->driver, "CMI8330/C3D"); |
537 | strcpy(card->shortname, "C-Media CMI8330/C3D"); | 589 | strcpy(card->shortname, "C-Media CMI8330/C3D"); |