// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for SoundBlaster 1.0/2.0/Pro soundcards and compatible * Copyright (c) by Jaroslav Kysela */ #include #include #include #include #include #include #include #include #include MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Sound Blaster 1.0/2.0/Pro"); MODULE_LICENSE("GPL"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3 */ module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for Sound Blaster soundcard."); module_param_array(id, charp, NULL, 0444); MODULE_PARM_DESC(id, "ID string for Sound Blaster soundcard."); module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable Sound Blaster soundcard."); module_param_hw_array(port, long, ioport, NULL, 0444); MODULE_PARM_DESC(port, "Port # for SB8 driver."); module_param_hw_array(irq, int, irq, NULL, 0444); MODULE_PARM_DESC(irq, "IRQ # for SB8 driver."); module_param_hw_array(dma8, int, dma, NULL, 0444); MODULE_PARM_DESC(dma8, "8-bit DMA # for SB8 driver."); struct snd_sb8 { struct resource *fm_res; /* used to block FM i/o region for legacy cards */ struct snd_sb *chip; }; static irqreturn_t snd_sb8_interrupt(int irq, void *dev_id) { struct snd_sb *chip = dev_id; if (chip->open & SB_OPEN_PCM) { return snd_sb8dsp_interrupt(chip); } else { return snd_sb8dsp_midi_interrupt(chip); } } static int snd_sb8_match(struct device *pdev, unsigned int dev) { if (!enable[dev]) return 0; if (irq[dev] == SNDRV_AUTO_IRQ) { dev_err(pdev, "please specify irq\n"); return 0; } if (dma8[dev] == SNDRV_AUTO_DMA) { dev_err(pdev, "please specify dma8\n"); return 0; } return 1; } static int snd_sb8_probe(struct device *pdev, unsigned int dev) { struct snd_sb *chip; struct snd_card *card; struct snd_sb8 *acard; struct snd_opl3 *opl3; int err; err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE, sizeof(struct snd_sb8), &card); if (err < 0) return err; acard = card->private_data; /* * Block the 0x388 port to avoid PnP conflicts. * No need to check this value after request_region, * as we never do anything with it. */ acard->fm_res = devm_request_region(card->dev, 0x388, 4, "SoundBlaster FM"); if (port[dev] != SNDRV_AUTO_PORT) { err = snd_sbdsp_create(card, port[dev], irq[dev], snd_sb8_interrupt, dma8[dev], -1, SB_HW_AUTO, &chip); if (err < 0) return err; } else { /* auto-probe legacy ports */ static const unsigned long possible_ports[] = { 0x220, 0x240, 0x260, }; int i; for (i = 0; i < ARRAY_SIZE(possible_ports); i++) { err = snd_sbdsp_create(card, possible_ports[i], irq[dev], snd_sb8_interrupt, dma8[dev], -1, SB_HW_AUTO, &chip); if (err >= 0) { port[dev] = possible_ports[i]; break; } } if (i >= ARRAY_SIZE(possible_ports)) return -EINVAL; } acard->chip = chip; if (chip->hardware >= SB_HW_16) { if (chip->hardware == SB_HW_ALS100) dev_warn(pdev, "ALS100 chip detected at 0x%lx, try snd-als100 module\n", port[dev]); else dev_warn(pdev, "SB 16 chip detected at 0x%lx, try snd-sb16 module\n", port[dev]); return -ENODEV; } err = snd_sb8dsp_pcm(chip, 0); if (err < 0) return err; err = snd_sbmixer_new(chip); if (err < 0) return err; if (chip->hardware == SB_HW_10 || chip->hardware == SB_HW_20) { err = snd_opl3_create(card, chip->port + 8, 0, OPL3_HW_AUTO, 1, &opl3); if (err < 0) dev_warn(pdev, "sb8: no OPL device at 0x%lx\n", chip->port + 8); } else { err = snd_opl3_create(card, chip->port, chip->port + 2, OPL3_HW_AUTO, 1, &opl3); if (err < 0) { dev_warn(pdev, "sb8: no OPL device at 0x%lx-0x%lx\n", chip->port, chip->port + 2); } } if (err >= 0) { err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); if (err < 0) return err; } err = snd_sb8dsp_midi(chip, 0); if (err < 0) return err; strcpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8"); strcpy(card->shortname, chip->name); sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d", chip->name, chip->port, irq[dev], dma8[dev]); err = snd_card_register(card); if (err < 0) return err; dev_set_drvdata(pdev, card); return 0; } #ifdef CONFIG_PM static int snd_sb8_suspend(struct device *dev, unsigned int n, pm_message_t state) { struct snd_card *card = dev_get_drvdata(dev); struct snd_sb8 *acard = card->private_data; struct snd_sb *chip = acard->chip; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); snd_sbmixer_suspend(chip); return 0; } static int snd_sb8_resume(struct device *dev, unsigned int n) { struct snd_card *card = dev_get_drvdata(dev); struct snd_sb8 *acard = card->private_data; struct snd_sb *chip = acard->chip; snd_sbdsp_reset(chip); snd_sbmixer_resume(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } #endif #define DEV_NAME "sb8" static struct isa_driver snd_sb8_driver = { .match = snd_sb8_match, .probe = snd_sb8_probe, #ifdef CONFIG_PM .suspend = snd_sb8_suspend, .resume = snd_sb8_resume, #endif .driver = { .name = DEV_NAME }, }; module_isa_driver(snd_sb8_driver, SNDRV_CARDS);