ALSA: opti-miro: add PnP detection

The PCM12 and PCM20 can be set into the ISA PnP mode. The PCM12 PnP
was sold as the PnP device.
Add code to handle detection of these cards using ISA PnP framework.

Tested on the PCM20 in PnP mode. The PCM12 PnP has the same MS Windows
INF file except for a card name displayed for user.

Signed-off-by: Krzysztof Helt <krzysztof.h1@wp.pl>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Krzysztof Helt 2009-11-30 07:46:56 +01:00 committed by Takashi Iwai
parent 70a5f1187b
commit 306ecee926

View file

@ -25,6 +25,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/isa.h> #include <linux/isa.h>
#include <linux/pnp.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/ioport.h> #include <linux/ioport.h>
@ -60,6 +61,9 @@ static int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */ static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
static int wss; static int wss;
static int ide; static int ide;
#ifdef CONFIG_PNP
static int isapnp = 1; /* Enable ISA PnP detection */
#endif
module_param(index, int, 0444); module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for miro soundcard."); MODULE_PARM_DESC(index, "Index value for miro soundcard.");
@ -83,6 +87,10 @@ module_param(wss, int, 0444);
MODULE_PARM_DESC(wss, "wss mode"); MODULE_PARM_DESC(wss, "wss mode");
module_param(ide, int, 0444); module_param(ide, int, 0444);
MODULE_PARM_DESC(ide, "enable ide port"); MODULE_PARM_DESC(ide, "enable ide port");
#ifdef CONFIG_PNP
module_param(isapnp, bool, 0444);
MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
#endif
#define OPTi9XX_HW_DETECT 0 #define OPTi9XX_HW_DETECT 0
#define OPTi9XX_HW_82C928 1 #define OPTi9XX_HW_82C928 1
@ -131,6 +139,21 @@ static char * snd_opti9xx_names[] = {
"82C930", "82C931", "82C933" "82C930", "82C931", "82C933"
}; };
static int snd_miro_pnp_is_probed;
#ifdef CONFIG_PNP
static struct pnp_card_device_id snd_miro_pnpids[] = {
/* PCM20 and PCM12 in PnP mode */
{ .id = "MIR0924",
.devs = { { "MIR0000" }, { "MIR0002" }, { "MIR0005" } }, },
{ .id = "" }
};
MODULE_DEVICE_TABLE(pnp_card, snd_miro_pnpids);
#endif /* CONFIG_PNP */
/* /*
* ACI control * ACI control
*/ */
@ -781,17 +804,23 @@ static int __devinit snd_miro_init(struct snd_miro *chip,
chip->mpu_port = -1; chip->mpu_port = -1;
chip->mpu_irq = -1; chip->mpu_irq = -1;
chip->pwd_reg = 3;
#ifdef CONFIG_PNP
if (isapnp && chip->mc_base)
/* PnP resource gives the least 10 bits */
chip->mc_base |= 0xc00;
else
#endif
chip->mc_base = 0xf8c;
switch (hardware) { switch (hardware) {
case OPTi9XX_HW_82C929: case OPTi9XX_HW_82C929:
chip->mc_base = 0xf8c;
chip->password = 0xe3; chip->password = 0xe3;
chip->pwd_reg = 3;
break; break;
case OPTi9XX_HW_82C924: case OPTi9XX_HW_82C924:
chip->mc_base = 0xf8c;
chip->password = 0xe5; chip->password = 0xe5;
chip->pwd_reg = 3;
break; break;
default: default:
@ -1014,17 +1043,22 @@ static int __devinit snd_miro_configure(struct snd_miro *chip)
return -EINVAL; return -EINVAL;
} }
switch (chip->wss_base) { /* PnP resource says it decodes only 10 bits of address */
case 0x530: switch (chip->wss_base & 0x3ff) {
case 0x130:
chip->wss_base = 0x530;
wss_base_bits = 0x00; wss_base_bits = 0x00;
break; break;
case 0x604: case 0x204:
chip->wss_base = 0x604;
wss_base_bits = 0x03; wss_base_bits = 0x03;
break; break;
case 0xe80: case 0x280:
chip->wss_base = 0xe80;
wss_base_bits = 0x01; wss_base_bits = 0x01;
break; break;
case 0xf40: case 0x340:
chip->wss_base = 0xf40;
wss_base_bits = 0x02; wss_base_bits = 0x02;
break; break;
default: default:
@ -1370,6 +1404,12 @@ static int __devinit snd_miro_probe(struct snd_card *card)
static int __devinit snd_miro_isa_match(struct device *devptr, unsigned int n) static int __devinit snd_miro_isa_match(struct device *devptr, unsigned int n)
{ {
#ifdef CONFIG_PNP
if (snd_miro_pnp_is_probed)
return 0;
if (isapnp)
return 0;
#endif
return 1; return 1;
} }
@ -1487,14 +1527,155 @@ static struct isa_driver snd_miro_driver = {
}, },
}; };
#ifdef CONFIG_PNP
static int __devinit snd_card_miro_pnp(struct snd_miro *chip,
struct pnp_card_link *card,
const struct pnp_card_device_id *pid)
{
struct pnp_dev *pdev;
int err;
struct pnp_dev *devmpu;
struct pnp_dev *devmc;
pdev = pnp_request_card_device(card, pid->devs[0].id, NULL);
if (pdev == NULL)
return -EBUSY;
devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
if (devmpu == NULL)
return -EBUSY;
devmc = pnp_request_card_device(card, pid->devs[2].id, NULL);
if (devmc == NULL)
return -EBUSY;
err = pnp_activate_dev(pdev);
if (err < 0) {
snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
return err;
}
err = pnp_activate_dev(devmc);
if (err < 0) {
snd_printk(KERN_ERR "OPL syntg pnp configure failure: %d\n",
err);
return err;
}
port = pnp_port_start(pdev, 1);
fm_port = pnp_port_start(pdev, 2) + 8;
/*
* The MC(0) is never accessed and the miroSOUND PCM20 card does not
* include it in the PnP resource range. OPTI93x include it.
*/
chip->mc_base = pnp_port_start(devmc, 0) - 1;
chip->mc_base_size = pnp_port_len(devmc, 0) + 1;
irq = pnp_irq(pdev, 0);
dma1 = pnp_dma(pdev, 0);
dma2 = pnp_dma(pdev, 1);
if (mpu_port > 0) {
err = pnp_activate_dev(devmpu);
if (err < 0) {
snd_printk(KERN_ERR "MPU401 pnp configure failure\n");
mpu_port = -1;
return err;
}
mpu_port = pnp_port_start(devmpu, 0);
mpu_irq = pnp_irq(devmpu, 0);
}
return 0;
}
static int __devinit snd_miro_pnp_probe(struct pnp_card_link *pcard,
const struct pnp_card_device_id *pid)
{
struct snd_card *card;
int err;
struct snd_miro *miro;
if (snd_miro_pnp_is_probed)
return -EBUSY;
if (!isapnp)
return -ENODEV;
err = snd_card_create(index, id, THIS_MODULE,
sizeof(struct snd_miro), &card);
if (err < 0)
return err;
card->private_free = snd_card_miro_free;
miro = card->private_data;
err = snd_card_miro_pnp(miro, pcard, pid);
if (err) {
snd_card_free(card);
return err;
}
/* only miroSOUND PCM20 and PCM12 == OPTi924 */
err = snd_miro_init(miro, OPTi9XX_HW_82C924);
if (err) {
snd_card_free(card);
return err;
}
err = snd_miro_opti_check(miro);
if (err) {
snd_printk(KERN_ERR "OPTI chip not found\n");
snd_card_free(card);
return err;
}
snd_card_set_dev(card, &pcard->card->dev);
err = snd_miro_probe(card);
if (err < 0) {
snd_card_free(card);
return err;
}
pnp_set_card_drvdata(pcard, card);
snd_miro_pnp_is_probed = 1;
return 0;
}
static void __devexit snd_miro_pnp_remove(struct pnp_card_link * pcard)
{
snd_card_free(pnp_get_card_drvdata(pcard));
pnp_set_card_drvdata(pcard, NULL);
snd_miro_pnp_is_probed = 0;
}
static struct pnp_card_driver miro_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
.name = "miro",
.id_table = snd_miro_pnpids,
.probe = snd_miro_pnp_probe,
.remove = __devexit_p(snd_miro_pnp_remove),
};
#endif
static int __init alsa_card_miro_init(void) static int __init alsa_card_miro_init(void)
{ {
#ifdef CONFIG_PNP
pnp_register_card_driver(&miro_pnpc_driver);
if (snd_miro_pnp_is_probed)
return 0;
pnp_unregister_card_driver(&miro_pnpc_driver);
#endif
return isa_register_driver(&snd_miro_driver, 1); return isa_register_driver(&snd_miro_driver, 1);
} }
static void __exit alsa_card_miro_exit(void) static void __exit alsa_card_miro_exit(void)
{ {
if (!snd_miro_pnp_is_probed) {
isa_unregister_driver(&snd_miro_driver); isa_unregister_driver(&snd_miro_driver);
return;
}
#ifdef CONFIG_PNP
pnp_unregister_card_driver(&miro_pnpc_driver);
#endif
} }
module_init(alsa_card_miro_init) module_init(alsa_card_miro_init)