linux/sound/oss/sb_card.c
<<
>>
Prefs
   1/*
   2 * sound/oss/sb_card.c
   3 *
   4 * Detection routine for the ISA Sound Blaster and compatable sound
   5 * cards.
   6 *
   7 * This file is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
   8 * Version 2 (June 1991). See the "COPYING" file distributed with this
   9 * software for more info.
  10 *
  11 * This is a complete rewrite of the detection routines. This was
  12 * prompted by the PnP API change during v2.5 and the ugly state the
  13 * code was in.
  14 *
  15 * Copyright (C) by Paul Laufer 2002. Based on code originally by
  16 * Hannu Savolainen which was modified by many others over the
  17 * years. Authors specifically mentioned in the previous version were:
  18 * Daniel Stone, Alessandro Zummo, Jeff Garzik, Arnaldo Carvalho de
  19 * Melo, Daniel Church, and myself.
  20 *
  21 * 02-05-2003 Original Release, Paul Laufer <paul@laufernet.com>
  22 * 02-07-2003 Bug made it into first release. Take two.
  23 */
  24
  25#include <linux/module.h>
  26#include <linux/moduleparam.h>
  27#include <linux/init.h>
  28#include "sound_config.h"
  29#include "sb_mixer.h"
  30#include "sb.h"
  31#ifdef CONFIG_PNP
  32#include <linux/pnp.h>
  33#endif /* CONFIG_PNP */
  34#include "sb_card.h"
  35
  36MODULE_DESCRIPTION("OSS Soundblaster ISA PnP and legacy sound driver");
  37MODULE_LICENSE("GPL");
  38
  39extern void *smw_free;
  40
  41static int __initdata mpu_io    = 0;
  42static int __initdata io        = -1;
  43static int __initdata irq       = -1;
  44static int __initdata dma       = -1;
  45static int __initdata dma16     = -1;
  46static int __initdata type      = 0; /* Can set this to a specific card type */
  47static int __initdata esstype   = 0; /* ESS chip type */
  48static int __initdata acer      = 0; /* Do acer notebook init? */
  49static int __initdata sm_games  = 0; /* Logitech soundman games? */
  50
  51static struct sb_card_config *legacy = NULL;
  52
  53#ifdef CONFIG_PNP
  54static int pnp_registered;
  55static int __initdata pnp       = 1;
  56/*
  57static int __initdata uart401   = 0;
  58*/
  59#else
  60static int __initdata pnp       = 0;
  61#endif
  62
  63module_param(io, int, 000);
  64MODULE_PARM_DESC(io,       "Soundblaster i/o base address (0x220,0x240,0x260,0x280)");
  65module_param(irq, int, 000);
  66MODULE_PARM_DESC(irq,      "IRQ (5,7,9,10)");
  67module_param(dma, int, 000);
  68MODULE_PARM_DESC(dma,      "8-bit DMA channel (0,1,3)");
  69module_param(dma16, int, 000);
  70MODULE_PARM_DESC(dma16,    "16-bit DMA channel (5,6,7)");
  71module_param(mpu_io, int, 000);
  72MODULE_PARM_DESC(mpu_io,   "MPU base address");
  73module_param(type, int, 000);
  74MODULE_PARM_DESC(type,     "You can set this to specific card type (doesn't " \
  75                 "work with pnp)");
  76module_param(sm_games, int, 000);
  77MODULE_PARM_DESC(sm_games, "Enable support for Logitech soundman games " \
  78                 "(doesn't work with pnp)");
  79module_param(esstype, int, 000);
  80MODULE_PARM_DESC(esstype,  "ESS chip type (doesn't work with pnp)");
  81module_param(acer, int, 000);
  82MODULE_PARM_DESC(acer,     "Set this to detect cards in some ACER notebooks "\
  83                 "(doesn't work with pnp)");
  84
  85#ifdef CONFIG_PNP
  86module_param(pnp, int, 000);
  87MODULE_PARM_DESC(pnp,     "Went set to 0 will disable detection using PnP. "\
  88                  "Default is 1.\n");
  89/* Not done yet.... */
  90/*
  91module_param(uart401, int, 000);
  92MODULE_PARM_DESC(uart401,  "When set to 1, will attempt to detect and enable"\
  93                 "the mpu on some clones");
  94*/
  95#endif /* CONFIG_PNP */
  96
  97/* OSS subsystem card registration shared by PnP and legacy routines */
  98static int sb_register_oss(struct sb_card_config *scc, struct sb_module_options *sbmo)
  99{
 100        if (!request_region(scc->conf.io_base, 16, "soundblaster")) {
 101                printk(KERN_ERR "sb: ports busy.\n");
 102                kfree(scc);
 103                return -EBUSY;
 104        }
 105
 106        if (!sb_dsp_detect(&scc->conf, 0, 0, sbmo)) {
 107                release_region(scc->conf.io_base, 16);
 108                printk(KERN_ERR "sb: Failed DSP Detect.\n");
 109                kfree(scc);
 110                return -ENODEV;
 111        }
 112        if(!sb_dsp_init(&scc->conf, THIS_MODULE)) {
 113                printk(KERN_ERR "sb: Failed DSP init.\n");
 114                kfree(scc);
 115                return -ENODEV;
 116        }
 117        if(scc->mpucnf.io_base > 0) {
 118                scc->mpu = 1;
 119                printk(KERN_INFO "sb: Turning on MPU\n");
 120                if(!probe_sbmpu(&scc->mpucnf, THIS_MODULE))
 121                        scc->mpu = 0;
 122        }
 123
 124        return 1;
 125}
 126
 127static void sb_unload(struct sb_card_config *scc)
 128{
 129        sb_dsp_unload(&scc->conf, 0);
 130        if(scc->mpu)
 131                unload_sbmpu(&scc->mpucnf);
 132        kfree(scc);
 133}
 134
 135/* Register legacy card with OSS subsystem */
 136static int __init sb_init_legacy(void)
 137{
 138        struct sb_module_options sbmo = {0};
 139
 140        if((legacy = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) {
 141                printk(KERN_ERR "sb: Error: Could not allocate memory\n");
 142                return -ENOMEM;
 143        }
 144
 145        legacy->conf.io_base      = io;
 146        legacy->conf.irq          = irq;
 147        legacy->conf.dma          = dma;
 148        legacy->conf.dma2         = dma16;
 149        legacy->conf.card_subtype = type;
 150
 151        legacy->mpucnf.io_base = mpu_io;
 152        legacy->mpucnf.irq     = -1;
 153        legacy->mpucnf.dma     = -1;
 154        legacy->mpucnf.dma2    = -1;
 155
 156        sbmo.esstype  = esstype;
 157        sbmo.sm_games = sm_games;
 158        sbmo.acer     = acer;
 159
 160        return sb_register_oss(legacy, &sbmo);
 161}
 162
 163#ifdef CONFIG_PNP
 164
 165/* Populate the OSS subsystem structures with information from PnP */
 166static void sb_dev2cfg(struct pnp_dev *dev, struct sb_card_config *scc)
 167{
 168        scc->conf.io_base   = -1;
 169        scc->conf.irq       = -1;
 170        scc->conf.dma       = -1;
 171        scc->conf.dma2      = -1;
 172        scc->mpucnf.io_base = -1;
 173        scc->mpucnf.irq     = -1;
 174        scc->mpucnf.dma     = -1;
 175        scc->mpucnf.dma2    = -1;
 176
 177        /* All clones layout their PnP tables differently and some use
 178           different logical devices for the MPU */
 179        if(!strncmp("CTL",scc->card_id,3)) {
 180                scc->conf.io_base   = pnp_port_start(dev,0);
 181                scc->conf.irq       = pnp_irq(dev,0);
 182                scc->conf.dma       = pnp_dma(dev,0);
 183                scc->conf.dma2      = pnp_dma(dev,1);
 184                scc->mpucnf.io_base = pnp_port_start(dev,1);
 185                return;
 186        }
 187        if(!strncmp("tBA",scc->card_id,3)) {
 188                scc->conf.io_base   = pnp_port_start(dev,0);
 189                scc->conf.irq       = pnp_irq(dev,0);
 190                scc->conf.dma       = pnp_dma(dev,0);
 191                scc->conf.dma2      = pnp_dma(dev,1);
 192                return;
 193        }
 194        if(!strncmp("ESS",scc->card_id,3)) {
 195                scc->conf.io_base   = pnp_port_start(dev,0);
 196                scc->conf.irq       = pnp_irq(dev,0);
 197                scc->conf.dma       = pnp_dma(dev,0);
 198                scc->conf.dma2      = pnp_dma(dev,1);
 199                scc->mpucnf.io_base = pnp_port_start(dev,2);
 200                return;
 201        }
 202        if(!strncmp("CMI",scc->card_id,3)) {
 203                scc->conf.io_base = pnp_port_start(dev,0);
 204                scc->conf.irq     = pnp_irq(dev,0);
 205                scc->conf.dma     = pnp_dma(dev,0);
 206                scc->conf.dma2    = pnp_dma(dev,1);
 207                return;
 208        }
 209        if(!strncmp("RWB",scc->card_id,3)) {
 210                scc->conf.io_base = pnp_port_start(dev,0);
 211                scc->conf.irq     = pnp_irq(dev,0);
 212                scc->conf.dma     = pnp_dma(dev,0);
 213                return;
 214        }
 215        if(!strncmp("ALS",scc->card_id,3)) {
 216                if(!strncmp("ALS0007",scc->card_id,7)) {
 217                        scc->conf.io_base = pnp_port_start(dev,0);
 218                        scc->conf.irq     = pnp_irq(dev,0);
 219                        scc->conf.dma     = pnp_dma(dev,0);
 220                } else {
 221                        scc->conf.io_base = pnp_port_start(dev,0);
 222                        scc->conf.irq     = pnp_irq(dev,0);
 223                        scc->conf.dma     = pnp_dma(dev,1);
 224                        scc->conf.dma2    = pnp_dma(dev,0);
 225                }
 226                return;
 227        }
 228        if(!strncmp("RTL",scc->card_id,3)) {
 229                scc->conf.io_base = pnp_port_start(dev,0);
 230                scc->conf.irq     = pnp_irq(dev,0);
 231                scc->conf.dma     = pnp_dma(dev,1);
 232                scc->conf.dma2    = pnp_dma(dev,0);
 233        }
 234}
 235
 236static unsigned int sb_pnp_devices;
 237
 238/* Probe callback function for the PnP API */
 239static int sb_pnp_probe(struct pnp_card_link *card, const struct pnp_card_device_id *card_id)
 240{
 241        struct sb_card_config *scc;
 242        struct sb_module_options sbmo = {0}; /* Default to 0 for PnP */
 243        struct pnp_dev *dev = pnp_request_card_device(card, card_id->devs[0].id, NULL);
 244        
 245        if(!dev){
 246                return -EBUSY;
 247        }
 248
 249        if((scc = kzalloc(sizeof(struct sb_card_config), GFP_KERNEL)) == NULL) {
 250                printk(KERN_ERR "sb: Error: Could not allocate memory\n");
 251                return -ENOMEM;
 252        }
 253
 254        printk(KERN_INFO "sb: PnP: Found Card Named = \"%s\", Card PnP id = " \
 255               "%s, Device PnP id = %s\n", card->card->name, card_id->id,
 256               dev->id->id);
 257
 258        scc->card_id = card_id->id;
 259        scc->dev_id = dev->id->id;
 260        sb_dev2cfg(dev, scc);
 261
 262        printk(KERN_INFO "sb: PnP:      Detected at: io=0x%x, irq=%d, " \
 263               "dma=%d, dma16=%d\n", scc->conf.io_base, scc->conf.irq,
 264               scc->conf.dma, scc->conf.dma2);
 265
 266        pnp_set_card_drvdata(card, scc);
 267        sb_pnp_devices++;
 268
 269        return sb_register_oss(scc, &sbmo);
 270}
 271
 272static void sb_pnp_remove(struct pnp_card_link *card)
 273{
 274        struct sb_card_config *scc = pnp_get_card_drvdata(card);
 275
 276        if(!scc)
 277                return;
 278
 279        printk(KERN_INFO "sb: PnP: Removing %s\n", scc->card_id);
 280
 281        sb_unload(scc);
 282}
 283
 284static struct pnp_card_driver sb_pnp_driver = {
 285        .name          = "OSS SndBlstr", /* 16 character limit */
 286        .id_table      = sb_pnp_card_table,
 287        .probe         = sb_pnp_probe,
 288        .remove        = sb_pnp_remove,
 289};
 290MODULE_DEVICE_TABLE(pnp_card, sb_pnp_card_table);
 291#endif /* CONFIG_PNP */
 292
 293static void sb_unregister_all(void)
 294{
 295#ifdef CONFIG_PNP
 296        if (pnp_registered)
 297                pnp_unregister_card_driver(&sb_pnp_driver);
 298#endif
 299}
 300
 301static int __init sb_init(void)
 302{
 303        int lres = 0;
 304        int pres = 0;
 305
 306        printk(KERN_INFO "sb: Init: Starting Probe...\n");
 307
 308        if(io != -1 && irq != -1 && dma != -1) {
 309                printk(KERN_INFO "sb: Probing legacy card with io=%x, "\
 310                       "irq=%d, dma=%d, dma16=%d\n",io, irq, dma, dma16);
 311                lres = sb_init_legacy();
 312        } else if((io != -1 || irq != -1 || dma != -1) ||
 313                  (!pnp && (io == -1 && irq == -1 && dma == -1)))
 314                printk(KERN_ERR "sb: Error: At least io, irq, and dma "\
 315                       "must be set for legacy cards.\n");
 316
 317#ifdef CONFIG_PNP
 318        if(pnp) {
 319                int err = pnp_register_card_driver(&sb_pnp_driver);
 320                if (!err)
 321                        pnp_registered = 1;
 322                pres = sb_pnp_devices;
 323        }
 324#endif
 325        printk(KERN_INFO "sb: Init: Done\n");
 326
 327        /* If either PnP or Legacy registered a card then return
 328         * success */
 329        if (pres == 0 && lres <= 0) {
 330                sb_unregister_all();
 331                return -ENODEV;
 332        }
 333        return 0;
 334}
 335
 336static void __exit sb_exit(void)
 337{
 338        printk(KERN_INFO "sb: Unloading...\n");
 339
 340        /* Unload legacy card */
 341        if (legacy) {
 342                printk (KERN_INFO "sb: Unloading legacy card\n");
 343                sb_unload(legacy);
 344        }
 345
 346        sb_unregister_all();
 347
 348        vfree(smw_free);
 349        smw_free = NULL;
 350}
 351
 352module_init(sb_init);
 353module_exit(sb_exit);
 354