Skip to content

Commit

Permalink
ALSA: opti-miro: add PnP detection
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Takashi Iwai <[email protected]>
  • Loading branch information
Krzysztof-H authored and tiwai committed Nov 30, 2009
1 parent 70a5f11 commit 306ecee
Showing 1 changed file with 192 additions and 11 deletions.
203 changes: 192 additions & 11 deletions sound/isa/opti9xx/miro.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/pnp.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/ioport.h>
Expand Down Expand Up @@ -60,6 +61,9 @@ static int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
static int wss;
static int ide;
#ifdef CONFIG_PNP
static int isapnp = 1; /* Enable ISA PnP detection */
#endif

module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for miro soundcard.");
Expand All @@ -83,6 +87,10 @@ module_param(wss, int, 0444);
MODULE_PARM_DESC(wss, "wss mode");
module_param(ide, int, 0444);
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_82C928 1
Expand Down Expand Up @@ -131,6 +139,21 @@ static char * snd_opti9xx_names[] = {
"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
*/
Expand Down Expand Up @@ -781,17 +804,23 @@ static int __devinit snd_miro_init(struct snd_miro *chip,
chip->mpu_port = -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) {
case OPTi9XX_HW_82C929:
chip->mc_base = 0xf8c;
chip->password = 0xe3;
chip->pwd_reg = 3;
break;

case OPTi9XX_HW_82C924:
chip->mc_base = 0xf8c;
chip->password = 0xe5;
chip->pwd_reg = 3;
break;

default:
Expand Down Expand Up @@ -1014,17 +1043,22 @@ static int __devinit snd_miro_configure(struct snd_miro *chip)
return -EINVAL;
}

switch (chip->wss_base) {
case 0x530:
/* PnP resource says it decodes only 10 bits of address */
switch (chip->wss_base & 0x3ff) {
case 0x130:
chip->wss_base = 0x530;
wss_base_bits = 0x00;
break;
case 0x604:
case 0x204:
chip->wss_base = 0x604;
wss_base_bits = 0x03;
break;
case 0xe80:
case 0x280:
chip->wss_base = 0xe80;
wss_base_bits = 0x01;
break;
case 0xf40:
case 0x340:
chip->wss_base = 0xf40;
wss_base_bits = 0x02;
break;
default:
Expand Down Expand Up @@ -1238,7 +1272,7 @@ static int __devinit snd_card_miro_aci_detect(struct snd_card *card,
static void snd_card_miro_free(struct snd_card *card)
{
struct snd_miro *miro = card->private_data;

release_and_free_resource(miro->res_aci_port);
if (miro->aci)
miro->aci->aci_port = 0;
Expand Down Expand Up @@ -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)
{
#ifdef CONFIG_PNP
if (snd_miro_pnp_is_probed)
return 0;
if (isapnp)
return 0;
#endif
return 1;
}

Expand Down Expand Up @@ -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)
{
#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);
}

static void __exit alsa_card_miro_exit(void)
{
isa_unregister_driver(&snd_miro_driver);
if (!snd_miro_pnp_is_probed) {
isa_unregister_driver(&snd_miro_driver);
return;
}
#ifdef CONFIG_PNP
pnp_unregister_card_driver(&miro_pnpc_driver);
#endif
}

module_init(alsa_card_miro_init)
Expand Down

0 comments on commit 306ecee

Please sign in to comment.