Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Driver for Gallant SC-6000 soundcard. This card is also known as
0004  *  Audio Excel DSP 16 or Zoltrix AV302.
0005  *  These cards use CompuMedia ASC-9308 chip + AD1848 codec.
0006  *  SC-6600 and SC-7000 cards are also supported. They are based on
0007  *  CompuMedia ASC-9408 chip and CS4231 codec.
0008  *
0009  *  Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl>
0010  *
0011  *  I don't have documentation for this card. I used the driver
0012  *  for OSS/Free included in the kernel source as reference.
0013  */
0014 
0015 #include <linux/module.h>
0016 #include <linux/delay.h>
0017 #include <linux/isa.h>
0018 #include <linux/io.h>
0019 #include <asm/dma.h>
0020 #include <sound/core.h>
0021 #include <sound/wss.h>
0022 #include <sound/opl3.h>
0023 #include <sound/mpu401.h>
0024 #include <sound/control.h>
0025 #define SNDRV_LEGACY_FIND_FREE_IRQ
0026 #define SNDRV_LEGACY_FIND_FREE_DMA
0027 #include <sound/initval.h>
0028 
0029 MODULE_AUTHOR("Krzysztof Helt");
0030 MODULE_DESCRIPTION("Gallant SC-6000");
0031 MODULE_LICENSE("GPL");
0032 
0033 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;  /* Index 0-MAX */
0034 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;   /* ID for this card */
0035 static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
0036 static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220, 0x240 */
0037 static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;    /* 5, 7, 9, 10, 11 */
0038 static long mss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530, 0xe80 */
0039 static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
0040                         /* 0x300, 0x310, 0x320, 0x330 */
0041 static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;    /* 5, 7, 9, 10, 0 */
0042 static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;    /* 0, 1, 3 */
0043 static bool joystick[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = false };
0044 
0045 module_param_array(index, int, NULL, 0444);
0046 MODULE_PARM_DESC(index, "Index value for sc-6000 based soundcard.");
0047 module_param_array(id, charp, NULL, 0444);
0048 MODULE_PARM_DESC(id, "ID string for sc-6000 based soundcard.");
0049 module_param_array(enable, bool, NULL, 0444);
0050 MODULE_PARM_DESC(enable, "Enable sc-6000 based soundcard.");
0051 module_param_hw_array(port, long, ioport, NULL, 0444);
0052 MODULE_PARM_DESC(port, "Port # for sc-6000 driver.");
0053 module_param_hw_array(mss_port, long, ioport, NULL, 0444);
0054 MODULE_PARM_DESC(mss_port, "MSS Port # for sc-6000 driver.");
0055 module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
0056 MODULE_PARM_DESC(mpu_port, "MPU-401 port # for sc-6000 driver.");
0057 module_param_hw_array(irq, int, irq, NULL, 0444);
0058 MODULE_PARM_DESC(irq, "IRQ # for sc-6000 driver.");
0059 module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
0060 MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver.");
0061 module_param_hw_array(dma, int, dma, NULL, 0444);
0062 MODULE_PARM_DESC(dma, "DMA # for sc-6000 driver.");
0063 module_param_array(joystick, bool, NULL, 0444);
0064 MODULE_PARM_DESC(joystick, "Enable gameport.");
0065 
0066 /*
0067  * Commands of SC6000's DSP (SBPRO+special).
0068  * Some of them are COMMAND_xx, in the future they may change.
0069  */
0070 #define WRITE_MDIRQ_CFG 0x50    /* Set M&I&DRQ mask (the real config)   */
0071 #define COMMAND_52  0x52    /*                  */
0072 #define READ_HARD_CFG   0x58    /* Read Hardware Config (I/O base etc)  */
0073 #define COMMAND_5C  0x5c    /*                  */
0074 #define COMMAND_60  0x60    /*                  */
0075 #define COMMAND_66  0x66    /*                  */
0076 #define COMMAND_6C  0x6c    /*                  */
0077 #define COMMAND_6E  0x6e    /*                  */
0078 #define COMMAND_88  0x88    /* Unknown command          */
0079 #define DSP_INIT_MSS    0x8c    /* Enable Microsoft Sound System mode   */
0080 #define COMMAND_C5  0xc5    /*                  */
0081 #define GET_DSP_VERSION 0xe1    /* Get DSP Version          */
0082 #define GET_DSP_COPYRIGHT 0xe3  /* Get DSP Copyright            */
0083 
0084 /*
0085  * Offsets of SC6000 DSP I/O ports. The offset is added to base I/O port
0086  * to have the actual I/O port.
0087  * Register permissions are:
0088  * (wo) == Write Only
0089  * (ro) == Read  Only
0090  * (w-) == Write
0091  * (r-) == Read
0092  */
0093 #define DSP_RESET   0x06    /* offset of DSP RESET      (wo) */
0094 #define DSP_READ    0x0a    /* offset of DSP READ       (ro) */
0095 #define DSP_WRITE   0x0c    /* offset of DSP WRITE      (w-) */
0096 #define DSP_COMMAND 0x0c    /* offset of DSP COMMAND    (w-) */
0097 #define DSP_STATUS  0x0c    /* offset of DSP STATUS     (r-) */
0098 #define DSP_DATAVAIL    0x0e    /* offset of DSP DATA AVAILABLE (ro) */
0099 
0100 #define PFX "sc6000: "
0101 #define DRV_NAME "SC-6000"
0102 
0103 /* hardware dependent functions */
0104 
0105 /*
0106  * sc6000_irq_to_softcfg - Decode irq number into cfg code.
0107  */
0108 static unsigned char sc6000_irq_to_softcfg(int irq)
0109 {
0110     unsigned char val = 0;
0111 
0112     switch (irq) {
0113     case 5:
0114         val = 0x28;
0115         break;
0116     case 7:
0117         val = 0x8;
0118         break;
0119     case 9:
0120         val = 0x10;
0121         break;
0122     case 10:
0123         val = 0x18;
0124         break;
0125     case 11:
0126         val = 0x20;
0127         break;
0128     default:
0129         break;
0130     }
0131     return val;
0132 }
0133 
0134 /*
0135  * sc6000_dma_to_softcfg - Decode dma number into cfg code.
0136  */
0137 static unsigned char sc6000_dma_to_softcfg(int dma)
0138 {
0139     unsigned char val = 0;
0140 
0141     switch (dma) {
0142     case 0:
0143         val = 1;
0144         break;
0145     case 1:
0146         val = 2;
0147         break;
0148     case 3:
0149         val = 3;
0150         break;
0151     default:
0152         break;
0153     }
0154     return val;
0155 }
0156 
0157 /*
0158  * sc6000_mpu_irq_to_softcfg - Decode MPU-401 irq number into cfg code.
0159  */
0160 static unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq)
0161 {
0162     unsigned char val = 0;
0163 
0164     switch (mpu_irq) {
0165     case 5:
0166         val = 4;
0167         break;
0168     case 7:
0169         val = 0x44;
0170         break;
0171     case 9:
0172         val = 0x84;
0173         break;
0174     case 10:
0175         val = 0xc4;
0176         break;
0177     default:
0178         break;
0179     }
0180     return val;
0181 }
0182 
0183 static int sc6000_wait_data(char __iomem *vport)
0184 {
0185     int loop = 1000;
0186     unsigned char val = 0;
0187 
0188     do {
0189         val = ioread8(vport + DSP_DATAVAIL);
0190         if (val & 0x80)
0191             return 0;
0192         cpu_relax();
0193     } while (loop--);
0194 
0195     return -EAGAIN;
0196 }
0197 
0198 static int sc6000_read(char __iomem *vport)
0199 {
0200     if (sc6000_wait_data(vport))
0201         return -EBUSY;
0202 
0203     return ioread8(vport + DSP_READ);
0204 
0205 }
0206 
0207 static int sc6000_write(char __iomem *vport, int cmd)
0208 {
0209     unsigned char val;
0210     int loop = 500000;
0211 
0212     do {
0213         val = ioread8(vport + DSP_STATUS);
0214         /*
0215          * DSP ready to receive data if bit 7 of val == 0
0216          */
0217         if (!(val & 0x80)) {
0218             iowrite8(cmd, vport + DSP_COMMAND);
0219             return 0;
0220         }
0221         cpu_relax();
0222     } while (loop--);
0223 
0224     snd_printk(KERN_ERR "DSP Command (0x%x) timeout.\n", cmd);
0225 
0226     return -EIO;
0227 }
0228 
0229 static int sc6000_dsp_get_answer(char __iomem *vport, int command,
0230                  char *data, int data_len)
0231 {
0232     int len = 0;
0233 
0234     if (sc6000_write(vport, command)) {
0235         snd_printk(KERN_ERR "CMD 0x%x: failed!\n", command);
0236         return -EIO;
0237     }
0238 
0239     do {
0240         int val = sc6000_read(vport);
0241 
0242         if (val < 0)
0243             break;
0244 
0245         data[len++] = val;
0246 
0247     } while (len < data_len);
0248 
0249     /*
0250      * If no more data available, return to the caller, no error if len>0.
0251      * We have no other way to know when the string is finished.
0252      */
0253     return len ? len : -EIO;
0254 }
0255 
0256 static int sc6000_dsp_reset(char __iomem *vport)
0257 {
0258     iowrite8(1, vport + DSP_RESET);
0259     udelay(10);
0260     iowrite8(0, vport + DSP_RESET);
0261     udelay(20);
0262     if (sc6000_read(vport) == 0xaa)
0263         return 0;
0264     return -ENODEV;
0265 }
0266 
0267 /* detection and initialization */
0268 static int sc6000_hw_cfg_write(char __iomem *vport, const int *cfg)
0269 {
0270     if (sc6000_write(vport, COMMAND_6C) < 0) {
0271         snd_printk(KERN_WARNING "CMD 0x%x: failed!\n", COMMAND_6C);
0272         return -EIO;
0273     }
0274     if (sc6000_write(vport, COMMAND_5C) < 0) {
0275         snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_5C);
0276         return -EIO;
0277     }
0278     if (sc6000_write(vport, cfg[0]) < 0) {
0279         snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[0]);
0280         return -EIO;
0281     }
0282     if (sc6000_write(vport, cfg[1]) < 0) {
0283         snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[1]);
0284         return -EIO;
0285     }
0286     if (sc6000_write(vport, COMMAND_C5) < 0) {
0287         snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_C5);
0288         return -EIO;
0289     }
0290 
0291     return 0;
0292 }
0293 
0294 static int sc6000_cfg_write(char __iomem *vport, unsigned char softcfg)
0295 {
0296 
0297     if (sc6000_write(vport, WRITE_MDIRQ_CFG)) {
0298         snd_printk(KERN_ERR "CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
0299         return -EIO;
0300     }
0301     if (sc6000_write(vport, softcfg)) {
0302         snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n");
0303         return -EIO;
0304     }
0305     return 0;
0306 }
0307 
0308 static int sc6000_setup_board(char __iomem *vport, int config)
0309 {
0310     int loop = 10;
0311 
0312     do {
0313         if (sc6000_write(vport, COMMAND_88)) {
0314             snd_printk(KERN_ERR "CMD 0x%x: failed!\n",
0315                    COMMAND_88);
0316             return -EIO;
0317         }
0318     } while ((sc6000_wait_data(vport) < 0) && loop--);
0319 
0320     if (sc6000_read(vport) < 0) {
0321         snd_printk(KERN_ERR "sc6000_read after CMD 0x%x: failed\n",
0322                COMMAND_88);
0323         return -EIO;
0324     }
0325 
0326     if (sc6000_cfg_write(vport, config))
0327         return -ENODEV;
0328 
0329     return 0;
0330 }
0331 
0332 static int sc6000_init_mss(char __iomem *vport, int config,
0333                char __iomem *vmss_port, int mss_config)
0334 {
0335     if (sc6000_write(vport, DSP_INIT_MSS)) {
0336         snd_printk(KERN_ERR "sc6000_init_mss [0x%x]: failed!\n",
0337                DSP_INIT_MSS);
0338         return -EIO;
0339     }
0340 
0341     msleep(10);
0342 
0343     if (sc6000_cfg_write(vport, config))
0344         return -EIO;
0345 
0346     iowrite8(mss_config, vmss_port);
0347 
0348     return 0;
0349 }
0350 
0351 static void sc6000_hw_cfg_encode(char __iomem *vport, int *cfg,
0352                  long xport, long xmpu,
0353                  long xmss_port, int joystick)
0354 {
0355     cfg[0] = 0;
0356     cfg[1] = 0;
0357     if (xport == 0x240)
0358         cfg[0] |= 1;
0359     if (xmpu != SNDRV_AUTO_PORT) {
0360         cfg[0] |= (xmpu & 0x30) >> 2;
0361         cfg[1] |= 0x20;
0362     }
0363     if (xmss_port == 0xe80)
0364         cfg[0] |= 0x10;
0365     cfg[0] |= 0x40;     /* always set */
0366     if (!joystick)
0367         cfg[0] |= 0x02;
0368     cfg[1] |= 0x80;     /* enable WSS system */
0369     cfg[1] &= ~0x40;    /* disable IDE */
0370     snd_printd("hw cfg %x, %x\n", cfg[0], cfg[1]);
0371 }
0372 
0373 static int sc6000_init_board(char __iomem *vport,
0374                  char __iomem *vmss_port, int dev)
0375 {
0376     char answer[15];
0377     char version[2];
0378     int mss_config = sc6000_irq_to_softcfg(irq[dev]) |
0379              sc6000_dma_to_softcfg(dma[dev]);
0380     int config = mss_config |
0381              sc6000_mpu_irq_to_softcfg(mpu_irq[dev]);
0382     int err;
0383     int old = 0;
0384 
0385     err = sc6000_dsp_reset(vport);
0386     if (err < 0) {
0387         snd_printk(KERN_ERR "sc6000_dsp_reset: failed!\n");
0388         return err;
0389     }
0390 
0391     memset(answer, 0, sizeof(answer));
0392     err = sc6000_dsp_get_answer(vport, GET_DSP_COPYRIGHT, answer, 15);
0393     if (err <= 0) {
0394         snd_printk(KERN_ERR "sc6000_dsp_copyright: failed!\n");
0395         return -ENODEV;
0396     }
0397     /*
0398      * My SC-6000 card return "SC-6000" in DSPCopyright, so
0399      * if we have something different, we have to be warned.
0400      */
0401     if (strncmp("SC-6000", answer, 7))
0402         snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n");
0403 
0404     if (sc6000_dsp_get_answer(vport, GET_DSP_VERSION, version, 2) < 2) {
0405         snd_printk(KERN_ERR "sc6000_dsp_version: failed!\n");
0406         return -ENODEV;
0407     }
0408     printk(KERN_INFO PFX "Detected model: %s, DSP version %d.%d\n",
0409         answer, version[0], version[1]);
0410 
0411     /* set configuration */
0412     sc6000_write(vport, COMMAND_5C);
0413     if (sc6000_read(vport) < 0)
0414         old = 1;
0415 
0416     if (!old) {
0417         int cfg[2];
0418         sc6000_hw_cfg_encode(vport, &cfg[0], port[dev], mpu_port[dev],
0419                      mss_port[dev], joystick[dev]);
0420         if (sc6000_hw_cfg_write(vport, cfg) < 0) {
0421             snd_printk(KERN_ERR "sc6000_hw_cfg_write: failed!\n");
0422             return -EIO;
0423         }
0424     }
0425     err = sc6000_setup_board(vport, config);
0426     if (err < 0) {
0427         snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
0428         return -ENODEV;
0429     }
0430 
0431     sc6000_dsp_reset(vport);
0432 
0433     if (!old) {
0434         sc6000_write(vport, COMMAND_60);
0435         sc6000_write(vport, 0x02);
0436         sc6000_dsp_reset(vport);
0437     }
0438 
0439     err = sc6000_setup_board(vport, config);
0440     if (err < 0) {
0441         snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
0442         return -ENODEV;
0443     }
0444     err = sc6000_init_mss(vport, config, vmss_port, mss_config);
0445     if (err < 0) {
0446         snd_printk(KERN_ERR "Cannot initialize "
0447                "Microsoft Sound System mode.\n");
0448         return -ENODEV;
0449     }
0450 
0451     return 0;
0452 }
0453 
0454 static int snd_sc6000_mixer(struct snd_wss *chip)
0455 {
0456     struct snd_card *card = chip->card;
0457     struct snd_ctl_elem_id id1, id2;
0458     int err;
0459 
0460     memset(&id1, 0, sizeof(id1));
0461     memset(&id2, 0, sizeof(id2));
0462     id1.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
0463     id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
0464     /* reassign AUX0 to FM */
0465     strcpy(id1.name, "Aux Playback Switch");
0466     strcpy(id2.name, "FM Playback Switch");
0467     err = snd_ctl_rename_id(card, &id1, &id2);
0468     if (err < 0)
0469         return err;
0470     strcpy(id1.name, "Aux Playback Volume");
0471     strcpy(id2.name, "FM Playback Volume");
0472     err = snd_ctl_rename_id(card, &id1, &id2);
0473     if (err < 0)
0474         return err;
0475     /* reassign AUX1 to CD */
0476     strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
0477     strcpy(id2.name, "CD Playback Switch");
0478     err = snd_ctl_rename_id(card, &id1, &id2);
0479     if (err < 0)
0480         return err;
0481     strcpy(id1.name, "Aux Playback Volume");
0482     strcpy(id2.name, "CD Playback Volume");
0483     err = snd_ctl_rename_id(card, &id1, &id2);
0484     if (err < 0)
0485         return err;
0486     return 0;
0487 }
0488 
0489 static int snd_sc6000_match(struct device *devptr, unsigned int dev)
0490 {
0491     if (!enable[dev])
0492         return 0;
0493     if (port[dev] == SNDRV_AUTO_PORT) {
0494         printk(KERN_ERR PFX "specify IO port\n");
0495         return 0;
0496     }
0497     if (mss_port[dev] == SNDRV_AUTO_PORT) {
0498         printk(KERN_ERR PFX "specify MSS port\n");
0499         return 0;
0500     }
0501     if (port[dev] != 0x220 && port[dev] != 0x240) {
0502         printk(KERN_ERR PFX "Port must be 0x220 or 0x240\n");
0503         return 0;
0504     }
0505     if (mss_port[dev] != 0x530 && mss_port[dev] != 0xe80) {
0506         printk(KERN_ERR PFX "MSS port must be 0x530 or 0xe80\n");
0507         return 0;
0508     }
0509     if (irq[dev] != SNDRV_AUTO_IRQ && !sc6000_irq_to_softcfg(irq[dev])) {
0510         printk(KERN_ERR PFX "invalid IRQ %d\n", irq[dev]);
0511         return 0;
0512     }
0513     if (dma[dev] != SNDRV_AUTO_DMA && !sc6000_dma_to_softcfg(dma[dev])) {
0514         printk(KERN_ERR PFX "invalid DMA %d\n", dma[dev]);
0515         return 0;
0516     }
0517     if (mpu_port[dev] != SNDRV_AUTO_PORT &&
0518         (mpu_port[dev] & ~0x30L) != 0x300) {
0519         printk(KERN_ERR PFX "invalid MPU-401 port %lx\n",
0520             mpu_port[dev]);
0521         return 0;
0522     }
0523     if (mpu_port[dev] != SNDRV_AUTO_PORT &&
0524         mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] != 0 &&
0525         !sc6000_mpu_irq_to_softcfg(mpu_irq[dev])) {
0526         printk(KERN_ERR PFX "invalid MPU-401 IRQ %d\n", mpu_irq[dev]);
0527         return 0;
0528     }
0529     return 1;
0530 }
0531 
0532 static void snd_sc6000_free(struct snd_card *card)
0533 {
0534     char __iomem *vport = (char __force __iomem *)card->private_data;
0535 
0536     if (vport)
0537         sc6000_setup_board(vport, 0);
0538 }
0539 
0540 static int __snd_sc6000_probe(struct device *devptr, unsigned int dev)
0541 {
0542     static const int possible_irqs[] = { 5, 7, 9, 10, 11, -1 };
0543     static const int possible_dmas[] = { 1, 3, 0, -1 };
0544     int err;
0545     int xirq = irq[dev];
0546     int xdma = dma[dev];
0547     struct snd_card *card;
0548     struct snd_wss *chip;
0549     struct snd_opl3 *opl3;
0550     char __iomem *vport;
0551     char __iomem *vmss_port;
0552 
0553     err = snd_devm_card_new(devptr, index[dev], id[dev], THIS_MODULE,
0554                 0, &card);
0555     if (err < 0)
0556         return err;
0557 
0558     if (xirq == SNDRV_AUTO_IRQ) {
0559         xirq = snd_legacy_find_free_irq(possible_irqs);
0560         if (xirq < 0) {
0561             snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
0562             return -EBUSY;
0563         }
0564     }
0565 
0566     if (xdma == SNDRV_AUTO_DMA) {
0567         xdma = snd_legacy_find_free_dma(possible_dmas);
0568         if (xdma < 0) {
0569             snd_printk(KERN_ERR PFX "unable to find a free DMA\n");
0570             return -EBUSY;
0571         }
0572     }
0573 
0574     if (!devm_request_region(devptr, port[dev], 0x10, DRV_NAME)) {
0575         snd_printk(KERN_ERR PFX
0576                "I/O port region is already in use.\n");
0577         return -EBUSY;
0578     }
0579     vport = devm_ioport_map(devptr, port[dev], 0x10);
0580     if (!vport) {
0581         snd_printk(KERN_ERR PFX
0582                "I/O port cannot be iomapped.\n");
0583         return -EBUSY;
0584     }
0585     card->private_data = (void __force *)vport;
0586 
0587     /* to make it marked as used */
0588     if (!devm_request_region(devptr, mss_port[dev], 4, DRV_NAME)) {
0589         snd_printk(KERN_ERR PFX
0590                "SC-6000 port I/O port region is already in use.\n");
0591         return -EBUSY;
0592     }
0593     vmss_port = devm_ioport_map(devptr, mss_port[dev], 4);
0594     if (!vmss_port) {
0595         snd_printk(KERN_ERR PFX
0596                "MSS port I/O cannot be iomapped.\n");
0597         return -EBUSY;
0598     }
0599 
0600     snd_printd("Initializing BASE[0x%lx] IRQ[%d] DMA[%d] MIRQ[%d]\n",
0601            port[dev], xirq, xdma,
0602            mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]);
0603 
0604     err = sc6000_init_board(vport, vmss_port, dev);
0605     if (err < 0)
0606         return err;
0607     card->private_free = snd_sc6000_free;
0608 
0609     err = snd_wss_create(card, mss_port[dev] + 4,  -1, xirq, xdma, -1,
0610                  WSS_HW_DETECT, 0, &chip);
0611     if (err < 0)
0612         return err;
0613 
0614     err = snd_wss_pcm(chip, 0);
0615     if (err < 0) {
0616         snd_printk(KERN_ERR PFX
0617                "error creating new WSS PCM device\n");
0618         return err;
0619     }
0620     err = snd_wss_mixer(chip);
0621     if (err < 0) {
0622         snd_printk(KERN_ERR PFX "error creating new WSS mixer\n");
0623         return err;
0624     }
0625     err = snd_sc6000_mixer(chip);
0626     if (err < 0) {
0627         snd_printk(KERN_ERR PFX "the mixer rewrite failed\n");
0628         return err;
0629     }
0630     if (snd_opl3_create(card,
0631                 0x388, 0x388 + 2,
0632                 OPL3_HW_AUTO, 0, &opl3) < 0) {
0633         snd_printk(KERN_ERR PFX "no OPL device at 0x%x-0x%x ?\n",
0634                0x388, 0x388 + 2);
0635     } else {
0636         err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
0637         if (err < 0)
0638             return err;
0639     }
0640 
0641     if (mpu_port[dev] != SNDRV_AUTO_PORT) {
0642         if (mpu_irq[dev] == SNDRV_AUTO_IRQ)
0643             mpu_irq[dev] = -1;
0644         if (snd_mpu401_uart_new(card, 0,
0645                     MPU401_HW_MPU401,
0646                     mpu_port[dev], 0,
0647                     mpu_irq[dev], NULL) < 0)
0648             snd_printk(KERN_ERR "no MPU-401 device at 0x%lx ?\n",
0649                     mpu_port[dev]);
0650     }
0651 
0652     strcpy(card->driver, DRV_NAME);
0653     strcpy(card->shortname, "SC-6000");
0654     sprintf(card->longname, "Gallant SC-6000 at 0x%lx, irq %d, dma %d",
0655         mss_port[dev], xirq, xdma);
0656 
0657     err = snd_card_register(card);
0658     if (err < 0)
0659         return err;
0660 
0661     dev_set_drvdata(devptr, card);
0662     return 0;
0663 }
0664 
0665 static int snd_sc6000_probe(struct device *devptr, unsigned int dev)
0666 {
0667     return snd_card_free_on_error(devptr, __snd_sc6000_probe(devptr, dev));
0668 }
0669 
0670 static struct isa_driver snd_sc6000_driver = {
0671     .match      = snd_sc6000_match,
0672     .probe      = snd_sc6000_probe,
0673     /* FIXME: suspend/resume */
0674     .driver     = {
0675         .name   = DRV_NAME,
0676     },
0677 };
0678 
0679 
0680 module_isa_driver(snd_sc6000_driver, SNDRV_CARDS);