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