linux/sound/isa/sb/sb8.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Driver for SoundBlaster 1.0/2.0/Pro soundcards and compatible
   4 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   5 */
   6
   7#include <linux/init.h>
   8#include <linux/err.h>
   9#include <linux/isa.h>
  10#include <linux/ioport.h>
  11#include <linux/module.h>
  12#include <sound/core.h>
  13#include <sound/sb.h>
  14#include <sound/opl3.h>
  15#include <sound/initval.h>
  16
  17MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
  18MODULE_DESCRIPTION("Sound Blaster 1.0/2.0/Pro");
  19MODULE_LICENSE("GPL");
  20
  21static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;      /* Index 0-MAX */
  22static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;       /* ID for this card */
  23static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
  24static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;     /* 0x220,0x240,0x260 */
  25static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;        /* 5,7,9,10 */
  26static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;       /* 1,3 */
  27
  28module_param_array(index, int, NULL, 0444);
  29MODULE_PARM_DESC(index, "Index value for Sound Blaster soundcard.");
  30module_param_array(id, charp, NULL, 0444);
  31MODULE_PARM_DESC(id, "ID string for Sound Blaster soundcard.");
  32module_param_array(enable, bool, NULL, 0444);
  33MODULE_PARM_DESC(enable, "Enable Sound Blaster soundcard.");
  34module_param_hw_array(port, long, ioport, NULL, 0444);
  35MODULE_PARM_DESC(port, "Port # for SB8 driver.");
  36module_param_hw_array(irq, int, irq, NULL, 0444);
  37MODULE_PARM_DESC(irq, "IRQ # for SB8 driver.");
  38module_param_hw_array(dma8, int, dma, NULL, 0444);
  39MODULE_PARM_DESC(dma8, "8-bit DMA # for SB8 driver.");
  40
  41struct snd_sb8 {
  42        struct resource *fm_res;        /* used to block FM i/o region for legacy cards */
  43        struct snd_sb *chip;
  44};
  45
  46static irqreturn_t snd_sb8_interrupt(int irq, void *dev_id)
  47{
  48        struct snd_sb *chip = dev_id;
  49
  50        if (chip->open & SB_OPEN_PCM) {
  51                return snd_sb8dsp_interrupt(chip);
  52        } else {
  53                return snd_sb8dsp_midi_interrupt(chip);
  54        }
  55}
  56
  57static int snd_sb8_match(struct device *pdev, unsigned int dev)
  58{
  59        if (!enable[dev])
  60                return 0;
  61        if (irq[dev] == SNDRV_AUTO_IRQ) {
  62                dev_err(pdev, "please specify irq\n");
  63                return 0;
  64        }
  65        if (dma8[dev] == SNDRV_AUTO_DMA) {
  66                dev_err(pdev, "please specify dma8\n");
  67                return 0;
  68        }
  69        return 1;
  70}
  71
  72static int snd_sb8_probe(struct device *pdev, unsigned int dev)
  73{
  74        struct snd_sb *chip;
  75        struct snd_card *card;
  76        struct snd_sb8 *acard;
  77        struct snd_opl3 *opl3;
  78        int err;
  79
  80        err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
  81                                sizeof(struct snd_sb8), &card);
  82        if (err < 0)
  83                return err;
  84        acard = card->private_data;
  85
  86        /*
  87         * Block the 0x388 port to avoid PnP conflicts.
  88         * No need to check this value after request_region,
  89         * as we never do anything with it.
  90         */
  91        acard->fm_res = devm_request_region(card->dev, 0x388, 4,
  92                                            "SoundBlaster FM");
  93
  94        if (port[dev] != SNDRV_AUTO_PORT) {
  95                err = snd_sbdsp_create(card, port[dev], irq[dev],
  96                                       snd_sb8_interrupt, dma8[dev],
  97                                       -1, SB_HW_AUTO, &chip);
  98                if (err < 0)
  99                        return err;
 100        } else {
 101                /* auto-probe legacy ports */
 102                static const unsigned long possible_ports[] = {
 103                        0x220, 0x240, 0x260,
 104                };
 105                int i;
 106                for (i = 0; i < ARRAY_SIZE(possible_ports); i++) {
 107                        err = snd_sbdsp_create(card, possible_ports[i],
 108                                               irq[dev],
 109                                               snd_sb8_interrupt,
 110                                               dma8[dev],
 111                                               -1,
 112                                               SB_HW_AUTO,
 113                                               &chip);
 114                        if (err >= 0) {
 115                                port[dev] = possible_ports[i];
 116                                break;
 117                        }
 118                }
 119                if (i >= ARRAY_SIZE(possible_ports))
 120                        return -EINVAL;
 121        }
 122        acard->chip = chip;
 123                        
 124        if (chip->hardware >= SB_HW_16) {
 125                if (chip->hardware == SB_HW_ALS100)
 126                        snd_printk(KERN_WARNING "ALS100 chip detected at 0x%lx, try snd-als100 module\n",
 127                                    port[dev]);
 128                else
 129                        snd_printk(KERN_WARNING "SB 16 chip detected at 0x%lx, try snd-sb16 module\n",
 130                                   port[dev]);
 131                return -ENODEV;
 132        }
 133
 134        err = snd_sb8dsp_pcm(chip, 0);
 135        if (err < 0)
 136                return err;
 137
 138        err = snd_sbmixer_new(chip);
 139        if (err < 0)
 140                return err;
 141
 142        if (chip->hardware == SB_HW_10 || chip->hardware == SB_HW_20) {
 143                err = snd_opl3_create(card, chip->port + 8, 0,
 144                                      OPL3_HW_AUTO, 1, &opl3);
 145                if (err < 0)
 146                        snd_printk(KERN_WARNING "sb8: no OPL device at 0x%lx\n", chip->port + 8);
 147        } else {
 148                err = snd_opl3_create(card, chip->port, chip->port + 2,
 149                                      OPL3_HW_AUTO, 1, &opl3);
 150                if (err < 0) {
 151                        snd_printk(KERN_WARNING "sb8: no OPL device at 0x%lx-0x%lx\n",
 152                                   chip->port, chip->port + 2);
 153                }
 154        }
 155        if (err >= 0) {
 156                err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
 157                if (err < 0)
 158                        return err;
 159        }
 160
 161        err = snd_sb8dsp_midi(chip, 0);
 162        if (err < 0)
 163                return err;
 164
 165        strcpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8");
 166        strcpy(card->shortname, chip->name);
 167        sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
 168                chip->name,
 169                chip->port,
 170                irq[dev], dma8[dev]);
 171
 172        err = snd_card_register(card);
 173        if (err < 0)
 174                return err;
 175
 176        dev_set_drvdata(pdev, card);
 177        return 0;
 178}
 179
 180#ifdef CONFIG_PM
 181static int snd_sb8_suspend(struct device *dev, unsigned int n,
 182                           pm_message_t state)
 183{
 184        struct snd_card *card = dev_get_drvdata(dev);
 185        struct snd_sb8 *acard = card->private_data;
 186        struct snd_sb *chip = acard->chip;
 187
 188        snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 189        snd_sbmixer_suspend(chip);
 190        return 0;
 191}
 192
 193static int snd_sb8_resume(struct device *dev, unsigned int n)
 194{
 195        struct snd_card *card = dev_get_drvdata(dev);
 196        struct snd_sb8 *acard = card->private_data;
 197        struct snd_sb *chip = acard->chip;
 198
 199        snd_sbdsp_reset(chip);
 200        snd_sbmixer_resume(chip);
 201        snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 202        return 0;
 203}
 204#endif
 205
 206#define DEV_NAME "sb8"
 207
 208static struct isa_driver snd_sb8_driver = {
 209        .match          = snd_sb8_match,
 210        .probe          = snd_sb8_probe,
 211#ifdef CONFIG_PM
 212        .suspend        = snd_sb8_suspend,
 213        .resume         = snd_sb8_resume,
 214#endif
 215        .driver         = {
 216                .name   = DEV_NAME 
 217        },
 218};
 219
 220module_isa_driver(snd_sb8_driver, SNDRV_CARDS);
 221