linux/sound/isa/sc6000.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Driver for Gallant SC-6000 soundcard. This card is also known as
   4 *  Audio Excel DSP 16 or Zoltrix AV302.
   5 *  These cards use CompuMedia ASC-9308 chip + AD1848 codec.
   6 *  SC-6600 and SC-7000 cards are also supported. They are based on
   7 *  CompuMedia ASC-9408 chip and CS4231 codec.
   8 *
   9 *  Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl>
  10 *
  11 *  I don't have documentation for this card. I used the driver
  12 *  for OSS/Free included in the kernel source as reference.
  13 */
  14
  15#include <linux/module.h>
  16#include <linux/delay.h>
  17#include <linux/isa.h>
  18#include <linux/io.h>
  19#include <asm/dma.h>
  20#include <sound/core.h>
  21#include <sound/wss.h>
  22#include <sound/opl3.h>
  23#include <sound/mpu401.h>
  24#include <sound/control.h>
  25#define SNDRV_LEGACY_FIND_FREE_IRQ
  26#define SNDRV_LEGACY_FIND_FREE_DMA
  27#include <sound/initval.h>
  28
  29MODULE_AUTHOR("Krzysztof Helt");
  30MODULE_DESCRIPTION("Gallant SC-6000");
  31MODULE_LICENSE("GPL");
  32MODULE_SUPPORTED_DEVICE("{{Gallant, SC-6000},"
  33                        "{AudioExcel, Audio Excel DSP 16},"
  34                        "{Zoltrix, AV302}}");
  35
  36static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;      /* Index 0-MAX */
  37static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;       /* ID for this card */
  38static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
  39static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;     /* 0x220, 0x240 */
  40static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;        /* 5, 7, 9, 10, 11 */
  41static long mss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530, 0xe80 */
  42static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
  43                                                /* 0x300, 0x310, 0x320, 0x330 */
  44static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;    /* 5, 7, 9, 10, 0 */
  45static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;        /* 0, 1, 3 */
  46static bool joystick[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = false };
  47
  48module_param_array(index, int, NULL, 0444);
  49MODULE_PARM_DESC(index, "Index value for sc-6000 based soundcard.");
  50module_param_array(id, charp, NULL, 0444);
  51MODULE_PARM_DESC(id, "ID string for sc-6000 based soundcard.");
  52module_param_array(enable, bool, NULL, 0444);
  53MODULE_PARM_DESC(enable, "Enable sc-6000 based soundcard.");
  54module_param_hw_array(port, long, ioport, NULL, 0444);
  55MODULE_PARM_DESC(port, "Port # for sc-6000 driver.");
  56module_param_hw_array(mss_port, long, ioport, NULL, 0444);
  57MODULE_PARM_DESC(mss_port, "MSS Port # for sc-6000 driver.");
  58module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
  59MODULE_PARM_DESC(mpu_port, "MPU-401 port # for sc-6000 driver.");
  60module_param_hw_array(irq, int, irq, NULL, 0444);
  61MODULE_PARM_DESC(irq, "IRQ # for sc-6000 driver.");
  62module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
  63MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver.");
  64module_param_hw_array(dma, int, dma, NULL, 0444);
  65MODULE_PARM_DESC(dma, "DMA # for sc-6000 driver.");
  66module_param_array(joystick, bool, NULL, 0444);
  67MODULE_PARM_DESC(joystick, "Enable gameport.");
  68
  69/*
  70 * Commands of SC6000's DSP (SBPRO+special).
  71 * Some of them are COMMAND_xx, in the future they may change.
  72 */
  73#define WRITE_MDIRQ_CFG 0x50    /* Set M&I&DRQ mask (the real config)   */
  74#define COMMAND_52      0x52    /*                                      */
  75#define READ_HARD_CFG   0x58    /* Read Hardware Config (I/O base etc)  */
  76#define COMMAND_5C      0x5c    /*                                      */
  77#define COMMAND_60      0x60    /*                                      */
  78#define COMMAND_66      0x66    /*                                      */
  79#define COMMAND_6C      0x6c    /*                                      */
  80#define COMMAND_6E      0x6e    /*                                      */
  81#define COMMAND_88      0x88    /* Unknown command                      */
  82#define DSP_INIT_MSS    0x8c    /* Enable Microsoft Sound System mode   */
  83#define COMMAND_C5      0xc5    /*                                      */
  84#define GET_DSP_VERSION 0xe1    /* Get DSP Version                      */
  85#define GET_DSP_COPYRIGHT 0xe3  /* Get DSP Copyright                    */
  86
  87/*
  88 * Offsets of SC6000 DSP I/O ports. The offset is added to base I/O port
  89 * to have the actual I/O port.
  90 * Register permissions are:
  91 * (wo) == Write Only
  92 * (ro) == Read  Only
  93 * (w-) == Write
  94 * (r-) == Read
  95 */
  96#define DSP_RESET       0x06    /* offset of DSP RESET          (wo) */
  97#define DSP_READ        0x0a    /* offset of DSP READ           (ro) */
  98#define DSP_WRITE       0x0c    /* offset of DSP WRITE          (w-) */
  99#define DSP_COMMAND     0x0c    /* offset of DSP COMMAND        (w-) */
 100#define DSP_STATUS      0x0c    /* offset of DSP STATUS         (r-) */
 101#define DSP_DATAVAIL    0x0e    /* offset of DSP DATA AVAILABLE (ro) */
 102
 103#define PFX "sc6000: "
 104#define DRV_NAME "SC-6000"
 105
 106/* hardware dependent functions */
 107
 108/*
 109 * sc6000_irq_to_softcfg - Decode irq number into cfg code.
 110 */
 111static unsigned char sc6000_irq_to_softcfg(int irq)
 112{
 113        unsigned char val = 0;
 114
 115        switch (irq) {
 116        case 5:
 117                val = 0x28;
 118                break;
 119        case 7:
 120                val = 0x8;
 121                break;
 122        case 9:
 123                val = 0x10;
 124                break;
 125        case 10:
 126                val = 0x18;
 127                break;
 128        case 11:
 129                val = 0x20;
 130                break;
 131        default:
 132                break;
 133        }
 134        return val;
 135}
 136
 137/*
 138 * sc6000_dma_to_softcfg - Decode dma number into cfg code.
 139 */
 140static unsigned char sc6000_dma_to_softcfg(int dma)
 141{
 142        unsigned char val = 0;
 143
 144        switch (dma) {
 145        case 0:
 146                val = 1;
 147                break;
 148        case 1:
 149                val = 2;
 150                break;
 151        case 3:
 152                val = 3;
 153                break;
 154        default:
 155                break;
 156        }
 157        return val;
 158}
 159
 160/*
 161 * sc6000_mpu_irq_to_softcfg - Decode MPU-401 irq number into cfg code.
 162 */
 163static unsigned char sc6000_mpu_irq_to_softcfg(int mpu_irq)
 164{
 165        unsigned char val = 0;
 166
 167        switch (mpu_irq) {
 168        case 5:
 169                val = 4;
 170                break;
 171        case 7:
 172                val = 0x44;
 173                break;
 174        case 9:
 175                val = 0x84;
 176                break;
 177        case 10:
 178                val = 0xc4;
 179                break;
 180        default:
 181                break;
 182        }
 183        return val;
 184}
 185
 186static int sc6000_wait_data(char __iomem *vport)
 187{
 188        int loop = 1000;
 189        unsigned char val = 0;
 190
 191        do {
 192                val = ioread8(vport + DSP_DATAVAIL);
 193                if (val & 0x80)
 194                        return 0;
 195                cpu_relax();
 196        } while (loop--);
 197
 198        return -EAGAIN;
 199}
 200
 201static int sc6000_read(char __iomem *vport)
 202{
 203        if (sc6000_wait_data(vport))
 204                return -EBUSY;
 205
 206        return ioread8(vport + DSP_READ);
 207
 208}
 209
 210static int sc6000_write(char __iomem *vport, int cmd)
 211{
 212        unsigned char val;
 213        int loop = 500000;
 214
 215        do {
 216                val = ioread8(vport + DSP_STATUS);
 217                /*
 218                 * DSP ready to receive data if bit 7 of val == 0
 219                 */
 220                if (!(val & 0x80)) {
 221                        iowrite8(cmd, vport + DSP_COMMAND);
 222                        return 0;
 223                }
 224                cpu_relax();
 225        } while (loop--);
 226
 227        snd_printk(KERN_ERR "DSP Command (0x%x) timeout.\n", cmd);
 228
 229        return -EIO;
 230}
 231
 232static int sc6000_dsp_get_answer(char __iomem *vport, int command,
 233                                 char *data, int data_len)
 234{
 235        int len = 0;
 236
 237        if (sc6000_write(vport, command)) {
 238                snd_printk(KERN_ERR "CMD 0x%x: failed!\n", command);
 239                return -EIO;
 240        }
 241
 242        do {
 243                int val = sc6000_read(vport);
 244
 245                if (val < 0)
 246                        break;
 247
 248                data[len++] = val;
 249
 250        } while (len < data_len);
 251
 252        /*
 253         * If no more data available, return to the caller, no error if len>0.
 254         * We have no other way to know when the string is finished.
 255         */
 256        return len ? len : -EIO;
 257}
 258
 259static int sc6000_dsp_reset(char __iomem *vport)
 260{
 261        iowrite8(1, vport + DSP_RESET);
 262        udelay(10);
 263        iowrite8(0, vport + DSP_RESET);
 264        udelay(20);
 265        if (sc6000_read(vport) == 0xaa)
 266                return 0;
 267        return -ENODEV;
 268}
 269
 270/* detection and initialization */
 271static int sc6000_hw_cfg_write(char __iomem *vport, const int *cfg)
 272{
 273        if (sc6000_write(vport, COMMAND_6C) < 0) {
 274                snd_printk(KERN_WARNING "CMD 0x%x: failed!\n", COMMAND_6C);
 275                return -EIO;
 276        }
 277        if (sc6000_write(vport, COMMAND_5C) < 0) {
 278                snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_5C);
 279                return -EIO;
 280        }
 281        if (sc6000_write(vport, cfg[0]) < 0) {
 282                snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[0]);
 283                return -EIO;
 284        }
 285        if (sc6000_write(vport, cfg[1]) < 0) {
 286                snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[1]);
 287                return -EIO;
 288        }
 289        if (sc6000_write(vport, COMMAND_C5) < 0) {
 290                snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_C5);
 291                return -EIO;
 292        }
 293
 294        return 0;
 295}
 296
 297static int sc6000_cfg_write(char __iomem *vport, unsigned char softcfg)
 298{
 299
 300        if (sc6000_write(vport, WRITE_MDIRQ_CFG)) {
 301                snd_printk(KERN_ERR "CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
 302                return -EIO;
 303        }
 304        if (sc6000_write(vport, softcfg)) {
 305                snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n");
 306                return -EIO;
 307        }
 308        return 0;
 309}
 310
 311static int sc6000_setup_board(char __iomem *vport, int config)
 312{
 313        int loop = 10;
 314
 315        do {
 316                if (sc6000_write(vport, COMMAND_88)) {
 317                        snd_printk(KERN_ERR "CMD 0x%x: failed!\n",
 318                                   COMMAND_88);
 319                        return -EIO;
 320                }
 321        } while ((sc6000_wait_data(vport) < 0) && loop--);
 322
 323        if (sc6000_read(vport) < 0) {
 324                snd_printk(KERN_ERR "sc6000_read after CMD 0x%x: failed\n",
 325                           COMMAND_88);
 326                return -EIO;
 327        }
 328
 329        if (sc6000_cfg_write(vport, config))
 330                return -ENODEV;
 331
 332        return 0;
 333}
 334
 335static int sc6000_init_mss(char __iomem *vport, int config,
 336                           char __iomem *vmss_port, int mss_config)
 337{
 338        if (sc6000_write(vport, DSP_INIT_MSS)) {
 339                snd_printk(KERN_ERR "sc6000_init_mss [0x%x]: failed!\n",
 340                           DSP_INIT_MSS);
 341                return -EIO;
 342        }
 343
 344        msleep(10);
 345
 346        if (sc6000_cfg_write(vport, config))
 347                return -EIO;
 348
 349        iowrite8(mss_config, vmss_port);
 350
 351        return 0;
 352}
 353
 354static void sc6000_hw_cfg_encode(char __iomem *vport, int *cfg,
 355                                 long xport, long xmpu,
 356                                 long xmss_port, int joystick)
 357{
 358        cfg[0] = 0;
 359        cfg[1] = 0;
 360        if (xport == 0x240)
 361                cfg[0] |= 1;
 362        if (xmpu != SNDRV_AUTO_PORT) {
 363                cfg[0] |= (xmpu & 0x30) >> 2;
 364                cfg[1] |= 0x20;
 365        }
 366        if (xmss_port == 0xe80)
 367                cfg[0] |= 0x10;
 368        cfg[0] |= 0x40;         /* always set */
 369        if (!joystick)
 370                cfg[0] |= 0x02;
 371        cfg[1] |= 0x80;         /* enable WSS system */
 372        cfg[1] &= ~0x40;        /* disable IDE */
 373        snd_printd("hw cfg %x, %x\n", cfg[0], cfg[1]);
 374}
 375
 376static int sc6000_init_board(char __iomem *vport,
 377                             char __iomem *vmss_port, int dev)
 378{
 379        char answer[15];
 380        char version[2];
 381        int mss_config = sc6000_irq_to_softcfg(irq[dev]) |
 382                         sc6000_dma_to_softcfg(dma[dev]);
 383        int config = mss_config |
 384                     sc6000_mpu_irq_to_softcfg(mpu_irq[dev]);
 385        int err;
 386        int old = 0;
 387
 388        err = sc6000_dsp_reset(vport);
 389        if (err < 0) {
 390                snd_printk(KERN_ERR "sc6000_dsp_reset: failed!\n");
 391                return err;
 392        }
 393
 394        memset(answer, 0, sizeof(answer));
 395        err = sc6000_dsp_get_answer(vport, GET_DSP_COPYRIGHT, answer, 15);
 396        if (err <= 0) {
 397                snd_printk(KERN_ERR "sc6000_dsp_copyright: failed!\n");
 398                return -ENODEV;
 399        }
 400        /*
 401         * My SC-6000 card return "SC-6000" in DSPCopyright, so
 402         * if we have something different, we have to be warned.
 403         */
 404        if (strncmp("SC-6000", answer, 7))
 405                snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n");
 406
 407        if (sc6000_dsp_get_answer(vport, GET_DSP_VERSION, version, 2) < 2) {
 408                snd_printk(KERN_ERR "sc6000_dsp_version: failed!\n");
 409                return -ENODEV;
 410        }
 411        printk(KERN_INFO PFX "Detected model: %s, DSP version %d.%d\n",
 412                answer, version[0], version[1]);
 413
 414        /* set configuration */
 415        sc6000_write(vport, COMMAND_5C);
 416        if (sc6000_read(vport) < 0)
 417                old = 1;
 418
 419        if (!old) {
 420                int cfg[2];
 421                sc6000_hw_cfg_encode(vport, &cfg[0], port[dev], mpu_port[dev],
 422                                     mss_port[dev], joystick[dev]);
 423                if (sc6000_hw_cfg_write(vport, cfg) < 0) {
 424                        snd_printk(KERN_ERR "sc6000_hw_cfg_write: failed!\n");
 425                        return -EIO;
 426                }
 427        }
 428        err = sc6000_setup_board(vport, config);
 429        if (err < 0) {
 430                snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
 431                return -ENODEV;
 432        }
 433
 434        sc6000_dsp_reset(vport);
 435
 436        if (!old) {
 437                sc6000_write(vport, COMMAND_60);
 438                sc6000_write(vport, 0x02);
 439                sc6000_dsp_reset(vport);
 440        }
 441
 442        err = sc6000_setup_board(vport, config);
 443        if (err < 0) {
 444                snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
 445                return -ENODEV;
 446        }
 447        err = sc6000_init_mss(vport, config, vmss_port, mss_config);
 448        if (err < 0) {
 449                snd_printk(KERN_ERR "Cannot initialize "
 450                           "Microsoft Sound System mode.\n");
 451                return -ENODEV;
 452        }
 453
 454        return 0;
 455}
 456
 457static int snd_sc6000_mixer(struct snd_wss *chip)
 458{
 459        struct snd_card *card = chip->card;
 460        struct snd_ctl_elem_id id1, id2;
 461        int err;
 462
 463        memset(&id1, 0, sizeof(id1));
 464        memset(&id2, 0, sizeof(id2));
 465        id1.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 466        id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 467        /* reassign AUX0 to FM */
 468        strcpy(id1.name, "Aux Playback Switch");
 469        strcpy(id2.name, "FM Playback Switch");
 470        err = snd_ctl_rename_id(card, &id1, &id2);
 471        if (err < 0)
 472                return err;
 473        strcpy(id1.name, "Aux Playback Volume");
 474        strcpy(id2.name, "FM Playback Volume");
 475        err = snd_ctl_rename_id(card, &id1, &id2);
 476        if (err < 0)
 477                return err;
 478        /* reassign AUX1 to CD */
 479        strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
 480        strcpy(id2.name, "CD Playback Switch");
 481        err = snd_ctl_rename_id(card, &id1, &id2);
 482        if (err < 0)
 483                return err;
 484        strcpy(id1.name, "Aux Playback Volume");
 485        strcpy(id2.name, "CD Playback Volume");
 486        err = snd_ctl_rename_id(card, &id1, &id2);
 487        if (err < 0)
 488                return err;
 489        return 0;
 490}
 491
 492static int snd_sc6000_match(struct device *devptr, unsigned int dev)
 493{
 494        if (!enable[dev])
 495                return 0;
 496        if (port[dev] == SNDRV_AUTO_PORT) {
 497                printk(KERN_ERR PFX "specify IO port\n");
 498                return 0;
 499        }
 500        if (mss_port[dev] == SNDRV_AUTO_PORT) {
 501                printk(KERN_ERR PFX "specify MSS port\n");
 502                return 0;
 503        }
 504        if (port[dev] != 0x220 && port[dev] != 0x240) {
 505                printk(KERN_ERR PFX "Port must be 0x220 or 0x240\n");
 506                return 0;
 507        }
 508        if (mss_port[dev] != 0x530 && mss_port[dev] != 0xe80) {
 509                printk(KERN_ERR PFX "MSS port must be 0x530 or 0xe80\n");
 510                return 0;
 511        }
 512        if (irq[dev] != SNDRV_AUTO_IRQ && !sc6000_irq_to_softcfg(irq[dev])) {
 513                printk(KERN_ERR PFX "invalid IRQ %d\n", irq[dev]);
 514                return 0;
 515        }
 516        if (dma[dev] != SNDRV_AUTO_DMA && !sc6000_dma_to_softcfg(dma[dev])) {
 517                printk(KERN_ERR PFX "invalid DMA %d\n", dma[dev]);
 518                return 0;
 519        }
 520        if (mpu_port[dev] != SNDRV_AUTO_PORT &&
 521            (mpu_port[dev] & ~0x30L) != 0x300) {
 522                printk(KERN_ERR PFX "invalid MPU-401 port %lx\n",
 523                        mpu_port[dev]);
 524                return 0;
 525        }
 526        if (mpu_port[dev] != SNDRV_AUTO_PORT &&
 527            mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] != 0 &&
 528            !sc6000_mpu_irq_to_softcfg(mpu_irq[dev])) {
 529                printk(KERN_ERR PFX "invalid MPU-401 IRQ %d\n", mpu_irq[dev]);
 530                return 0;
 531        }
 532        return 1;
 533}
 534
 535static int snd_sc6000_probe(struct device *devptr, unsigned int dev)
 536{
 537        static int possible_irqs[] = { 5, 7, 9, 10, 11, -1 };
 538        static int possible_dmas[] = { 1, 3, 0, -1 };
 539        int err;
 540        int xirq = irq[dev];
 541        int xdma = dma[dev];
 542        struct snd_card *card;
 543        struct snd_wss *chip;
 544        struct snd_opl3 *opl3;
 545        char __iomem **vport;
 546        char __iomem *vmss_port;
 547
 548
 549        err = snd_card_new(devptr, index[dev], id[dev], THIS_MODULE,
 550                           sizeof(vport), &card);
 551        if (err < 0)
 552                return err;
 553
 554        vport = card->private_data;
 555        if (xirq == SNDRV_AUTO_IRQ) {
 556                xirq = snd_legacy_find_free_irq(possible_irqs);
 557                if (xirq < 0) {
 558                        snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
 559                        err = -EBUSY;
 560                        goto err_exit;
 561                }
 562        }
 563
 564        if (xdma == SNDRV_AUTO_DMA) {
 565                xdma = snd_legacy_find_free_dma(possible_dmas);
 566                if (xdma < 0) {
 567                        snd_printk(KERN_ERR PFX "unable to find a free DMA\n");
 568                        err = -EBUSY;
 569                        goto err_exit;
 570                }
 571        }
 572
 573        if (!request_region(port[dev], 0x10, DRV_NAME)) {
 574                snd_printk(KERN_ERR PFX
 575                           "I/O port region is already in use.\n");
 576                err = -EBUSY;
 577                goto err_exit;
 578        }
 579        *vport = devm_ioport_map(devptr, port[dev], 0x10);
 580        if (*vport == NULL) {
 581                snd_printk(KERN_ERR PFX
 582                           "I/O port cannot be iomapped.\n");
 583                err = -EBUSY;
 584                goto err_unmap1;
 585        }
 586
 587        /* to make it marked as used */
 588        if (!request_region(mss_port[dev], 4, DRV_NAME)) {
 589                snd_printk(KERN_ERR PFX
 590                           "SC-6000 port I/O port region is already in use.\n");
 591                err = -EBUSY;
 592                goto err_unmap1;
 593        }
 594        vmss_port = devm_ioport_map(devptr, mss_port[dev], 4);
 595        if (!vmss_port) {
 596                snd_printk(KERN_ERR PFX
 597                           "MSS port I/O cannot be iomapped.\n");
 598                err = -EBUSY;
 599                goto err_unmap2;
 600        }
 601
 602        snd_printd("Initializing BASE[0x%lx] IRQ[%d] DMA[%d] MIRQ[%d]\n",
 603                   port[dev], xirq, xdma,
 604                   mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]);
 605
 606        err = sc6000_init_board(*vport, vmss_port, dev);
 607        if (err < 0)
 608                goto err_unmap2;
 609
 610        err = snd_wss_create(card, mss_port[dev] + 4,  -1, xirq, xdma, -1,
 611                             WSS_HW_DETECT, 0, &chip);
 612        if (err < 0)
 613                goto err_unmap2;
 614
 615        err = snd_wss_pcm(chip, 0);
 616        if (err < 0) {
 617                snd_printk(KERN_ERR PFX
 618                           "error creating new WSS PCM device\n");
 619                goto err_unmap2;
 620        }
 621        err = snd_wss_mixer(chip);
 622        if (err < 0) {
 623                snd_printk(KERN_ERR PFX "error creating new WSS mixer\n");
 624                goto err_unmap2;
 625        }
 626        err = snd_sc6000_mixer(chip);
 627        if (err < 0) {
 628                snd_printk(KERN_ERR PFX "the mixer rewrite failed\n");
 629                goto err_unmap2;
 630        }
 631        if (snd_opl3_create(card,
 632                            0x388, 0x388 + 2,
 633                            OPL3_HW_AUTO, 0, &opl3) < 0) {
 634                snd_printk(KERN_ERR PFX "no OPL device at 0x%x-0x%x ?\n",
 635                           0x388, 0x388 + 2);
 636        } else {
 637                err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
 638                if (err < 0)
 639                        goto err_unmap2;
 640        }
 641
 642        if (mpu_port[dev] != SNDRV_AUTO_PORT) {
 643                if (mpu_irq[dev] == SNDRV_AUTO_IRQ)
 644                        mpu_irq[dev] = -1;
 645                if (snd_mpu401_uart_new(card, 0,
 646                                        MPU401_HW_MPU401,
 647                                        mpu_port[dev], 0,
 648                                        mpu_irq[dev], NULL) < 0)
 649                        snd_printk(KERN_ERR "no MPU-401 device at 0x%lx ?\n",
 650                                        mpu_port[dev]);
 651        }
 652
 653        strcpy(card->driver, DRV_NAME);
 654        strcpy(card->shortname, "SC-6000");
 655        sprintf(card->longname, "Gallant SC-6000 at 0x%lx, irq %d, dma %d",
 656                mss_port[dev], xirq, xdma);
 657
 658        err = snd_card_register(card);
 659        if (err < 0)
 660                goto err_unmap2;
 661
 662        dev_set_drvdata(devptr, card);
 663        return 0;
 664
 665err_unmap2:
 666        sc6000_setup_board(*vport, 0);
 667        release_region(mss_port[dev], 4);
 668err_unmap1:
 669        release_region(port[dev], 0x10);
 670err_exit:
 671        snd_card_free(card);
 672        return err;
 673}
 674
 675static int snd_sc6000_remove(struct device *devptr, unsigned int dev)
 676{
 677        struct snd_card *card = dev_get_drvdata(devptr);
 678        char __iomem **vport = card->private_data;
 679
 680        if (sc6000_setup_board(*vport, 0) < 0)
 681                snd_printk(KERN_WARNING "sc6000_setup_board failed on exit!\n");
 682
 683        release_region(port[dev], 0x10);
 684        release_region(mss_port[dev], 4);
 685
 686        snd_card_free(card);
 687        return 0;
 688}
 689
 690static struct isa_driver snd_sc6000_driver = {
 691        .match          = snd_sc6000_match,
 692        .probe          = snd_sc6000_probe,
 693        .remove         = snd_sc6000_remove,
 694        /* FIXME: suspend/resume */
 695        .driver         = {
 696                .name   = DRV_NAME,
 697        },
 698};
 699
 700
 701module_isa_driver(snd_sc6000_driver, SNDRV_CARDS);
 702