/* * sound/sb_card.c * * Detection routine for the Sound Blaster cards. * * * Copyright (C) by Hannu Savolainen 1993-1997 * * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. * * * 26-11-1999 Patched to compile without ISA PnP support in the * kernel - Daniel Stone (tamriel@ductape.net) * * 06-01-2000 Refined and bugfixed ISA PnP support, added * CMI 8330 support - Alessandro Zummo * * 18-01-2000 Separated sb_card and sb_common * Jeff Garzik * * 04-02-2000 Added Soundblaster AWE 64 PnP support, isapnpjump * Alessandro Zummo * * 11-02-2000 Added Soundblaster AWE 32 PnP support, refined PnP code * Alessandro Zummo * * 13-02-2000 Hopefully fixed awe/sb16 related bugs, code cleanup * Alessandro Zummo * * 13-03-2000 Added some more cards, thanks to Torsten Werner. * Removed joystick and wavetable code, there are better places for them. * Code cleanup plus some fixes. * */ #include #include #include #include #include #include "sound_config.h" #include "soundmodule.h" #include "sb_mixer.h" #include "sb.h" static int sbmpu = 0; extern void *smw_free; static void __init attach_sb_card(struct address_info *hw_config) { if(!sb_dsp_init(hw_config)) hw_config->slots[0] = -1; SOUND_LOCK; } static int __init probe_sb(struct address_info *hw_config) { if (hw_config->io_base == -1 || hw_config->dma == -1 || hw_config->irq == -1) { printk(KERN_ERR "sb_card: I/O, IRQ, and DMA are mandatory\n"); return -EINVAL; } #ifdef CONFIG_MCA /* MCA code added by ZP Gu (zpg@castle.net) */ if (MCA_bus) { /* no multiple REPLY card probing */ int slot; u8 pos2, pos3, pos4; slot = mca_find_adapter( 0x5138, 0 ); if( slot == MCA_NOTFOUND ) { slot = mca_find_adapter( 0x5137, 0 ); if (slot != MCA_NOTFOUND) mca_set_adapter_name( slot, "REPLY SB16 & SCSI Adapter" ); } else { mca_set_adapter_name( slot, "REPLY SB16 Adapter" ); } if (slot != MCA_NOTFOUND) { mca_mark_as_used(slot); pos2 = mca_read_stored_pos( slot, 2 ); pos3 = mca_read_stored_pos( slot, 3 ); pos4 = mca_read_stored_pos( slot, 4 ); if (pos2 & 0x4) { /* enabled? */ static unsigned short irq[] = { 0, 5, 7, 10 }; /* static unsigned short midiaddr[] = {0, 0x330, 0, 0x300 }; */ hw_config->io_base = 0x220 + 0x20 * (pos2 >> 6); hw_config->irq = irq[(pos4 >> 5) & 0x3]; hw_config->dma = pos3 & 0xf; /* Reply ADF wrong on High DMA, pos[1] should start w/ 00 */ hw_config->dma2 = (pos3 >> 4) & 0x3; if (hw_config->dma2 == 0) hw_config->dma2 = hw_config->dma; else hw_config->dma2 += 4; /* hw_config->driver_use_2 = midiaddr[(pos2 >> 3) & 0x3]; */ printk(KERN_INFO "sb: Reply MCA SB at slot=%d \ iobase=0x%x irq=%d lo_dma=%d hi_dma=%d\n", slot+1, hw_config->io_base, hw_config->irq, hw_config->dma, hw_config->dma2); } else { printk (KERN_INFO "sb: Reply SB Base I/O address disabled\n"); } } } #endif /* This is useless since is done by sb_dsp_detect - azummo */ if (check_region(hw_config->io_base, 16)) { printk(KERN_ERR "sb_card: I/O port 0x%x is already in use\n\n", hw_config->io_base); return 0; } return sb_dsp_detect(hw_config, 0, 0); } static void __exit unload_sb(struct address_info *hw_config) { if(hw_config->slots[0]!=-1) sb_dsp_unload(hw_config, sbmpu); } extern int esstype; /* ESS chip type */ static struct address_info cfg; static struct address_info cfg_mpu; struct pci_dev *sb_dev = NULL, *mpu_dev = NULL; /* * Note DMA2 of -1 has the right meaning in the SB16 driver as well * as here. It will cause either an error if it is needed or a fallback * to the 8bit channel. */ static int __initdata mpu_io = 0; static int __initdata io = -1; static int __initdata irq = -1; static int __initdata dma = -1; static int __initdata dma16 = -1; /* Set this for modules that need it */ static int __initdata type = 0; /* Can set this to a specific card type */ #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE static int isapnp = 1; static int isapnpjump = 0; static int activated = 1; #else static int isapnp = 0; #endif MODULE_DESCRIPTION("Soundblaster driver"); MODULE_PARM(io, "i"); MODULE_PARM(irq, "i"); MODULE_PARM(dma, "i"); MODULE_PARM(dma16, "i"); MODULE_PARM(mpu_io, "i"); MODULE_PARM(type, "i"); MODULE_PARM(sm_games, "i"); MODULE_PARM(esstype, "i"); MODULE_PARM(acer, "i"); #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE MODULE_PARM(isapnp, "i"); MODULE_PARM(isapnpjump, "i"); MODULE_PARM_DESC(isapnp, "When set to 0, Plug & Play support will be disabled"); MODULE_PARM_DESC(isapnpjump, "Jumps to a specific slot in the driver's PnP table. Use the source, Luke."); #endif MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)"); MODULE_PARM_DESC(irq, "IRQ (5,7,9,10)"); MODULE_PARM_DESC(dma, "8-bit DMA channel (0,1,3)"); MODULE_PARM_DESC(dma16, "16-bit DMA channel (5,6,7)"); MODULE_PARM_DESC(mpu_io, "Mpu base address"); MODULE_PARM_DESC(type, "You can set this to specific card type"); MODULE_PARM_DESC(sm_games, "Enable support for Logitech soundman games"); MODULE_PARM_DESC(esstype, "ESS chip type"); MODULE_PARM_DESC(acer, "Set this to detect cards in some ACER notebooks"); #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE /* That's useful. */ #define show_base(devname, resname, resptr) printk(KERN_INFO "sb: %s %s base located at %#lx\n", devname, resname, (resptr)->start) static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev *dev) { int err; /* Device already active? Let's use it */ if(dev->active) { activated = 0; return(dev); } if((err = dev->activate(dev)) < 0) { printk(KERN_ERR "sb: %s %s config failed (out of resources?)[%d]\n", devname, resname, err); dev->deactivate(dev); return(NULL); } return(dev); } /* Card's specific initialization functions */ static struct pci_dev *sb_init_generic(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) { if((sb_dev = isapnp_find_dev(bus, card->vendor, card->device, NULL))) { sb_dev->prepare(sb_dev); if((sb_dev = activate_dev("Soundblaster", "sb", sb_dev))) { hw_config->io_base = sb_dev->resource[0].start; hw_config->irq = sb_dev->irq_resource[0].start; hw_config->dma = sb_dev->dma_resource[0].start; hw_config->dma2 = sb_dev->dma_resource[1].start; mpu_config->io_base = sb_dev->resource[1].start; } } return(sb_dev); } static struct pci_dev *sb_init_ess(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) { if((sb_dev = isapnp_find_dev(bus, card->vendor, card->device, NULL))) { sb_dev->prepare(sb_dev); if((sb_dev = activate_dev("ESS", "sb", sb_dev))) { hw_config->io_base = sb_dev->resource[0].start; hw_config->irq = sb_dev->irq_resource[0].start; hw_config->dma = sb_dev->dma_resource[0].start; hw_config->dma2 = sb_dev->dma_resource[1].start; mpu_config->io_base = sb_dev->resource[2].start; } } return(sb_dev); } static struct pci_dev *sb_init_cmi(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) { /* * The CMI8330/C3D is a very 'stupid' chip... where did they get al those @@@ ? * It's ISAPnP section is badly designed and has many flaws, i'll do my best * to workaround them. I strongly suggest you to buy a real soundcard. * The CMI8330 on my motherboard has also the bad habit to activate * the rear channel of my amplifier instead of the front one. */ /* @X@0001:Soundblaster. */ if((sb_dev = isapnp_find_dev(bus, ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL))) { sb_dev->prepare(sb_dev); if((sb_dev = activate_dev("CMI8330", "sb", sb_dev))) { hw_config->io_base = sb_dev->resource[0].start; hw_config->irq = sb_dev->irq_resource[0].start; hw_config->dma = sb_dev->dma_resource[0].start; hw_config->dma2 = sb_dev->dma_resource[1].start; show_base("CMI8330", "sb", &sb_dev->resource[0]); } if(!sb_dev) return(NULL); } else printk(KERN_ERR "sb: CMI8330 panic: sb base not found\n"); /* @H@0001:mpu */ if((mpu_dev = isapnp_find_dev(bus, ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), NULL))) { mpu_dev->prepare(mpu_dev); /* This disables the interrupt on this resource. Do we need it ? */ mpu_dev->irq_resource[0].flags = 0; if((mpu_dev = activate_dev("CMI8330", "mpu", mpu_dev))) { show_base("CMI8330", "mpu", &mpu_dev->resource[0]); mpu_config->io_base = mpu_dev->resource[0].start; } } else printk(KERN_ERR "sb: CMI8330 panic: mpu not found\n"); printk(KERN_INFO "sb: CMI8330 mail reports to Alessandro Zummo \n"); return(sb_dev); } static struct pci_dev *sb_init_diamond(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) { /* * Diamonds DT0197H * very similar to the CMI8330 above */ /* @@@0001:Soundblaster. */ if((sb_dev = isapnp_find_dev(bus, ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), NULL))) { sb_dev->prepare(sb_dev); if((sb_dev = activate_dev("DT0197H", "sb", sb_dev))) { hw_config->io_base = sb_dev->resource[0].start; hw_config->irq = sb_dev->irq_resource[0].start; hw_config->dma = sb_dev->dma_resource[0].start; hw_config->dma2 = -1; show_base("DT0197H", "sb", &sb_dev->resource[0]); } if(!sb_dev) return(NULL); } else printk(KERN_ERR "sb: DT0197H panic: sb base not found\n"); /* @X@0001:mpu */ if((mpu_dev = isapnp_find_dev(bus, ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL))) { mpu_dev->prepare(mpu_dev); if((mpu_dev = activate_dev("DT0197H", "mpu", mpu_dev))) { show_base("DT0197H", "mpu", &mpu_dev->resource[0]); mpu_config->io_base = mpu_dev->resource[0].start; } } else printk(KERN_ERR "sb: DT0197H panic: mpu not found\n"); printk(KERN_INFO "sb: DT0197H mail reports to Torsten Werner \n"); return(sb_dev); } static struct pci_dev *sb_init_als(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) { /* * ALS100 * very similar to both ones above above */ /* @@@0001:Soundblaster. */ if((sb_dev = isapnp_find_dev(bus, ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), NULL))) { sb_dev->prepare(sb_dev); if((sb_dev = activate_dev("ALS100", "sb", sb_dev))) { hw_config->io_base = sb_dev->resource[0].start; hw_config->irq = sb_dev->irq_resource[0].start; hw_config->dma = sb_dev->dma_resource[1].start; hw_config->dma2 = sb_dev->dma_resource[0].start; show_base("ALS100", "sb", &sb_dev->resource[0]); } if(!sb_dev) return(NULL); } else printk(KERN_ERR "sb: ALS100 panic: sb base not found\n"); /* @X@0001:mpu */ if((mpu_dev = isapnp_find_dev(bus, ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL))) { mpu_dev->prepare(mpu_dev); if((mpu_dev = activate_dev("ALS100", "mpu", mpu_dev))) { show_base("ALS100", "mpu", &mpu_dev->resource[0]); mpu_config->io_base = mpu_dev->resource[0].start; } } else printk(KERN_ERR "sb: ALS100 panic: mpu not found\n"); printk(KERN_INFO "sb: ALS100 mail reports to Torsten Werner \n"); return(sb_dev); } #define SBF_DEV 0x01 /* Please notice that cards without this flag are on the top in the list */ static struct { unsigned short vendor, function, flags; struct pci_dev * (*initfunc)(struct pci_bus *, struct pci_dev *, struct address_info *, struct address_info *); char *name; } sb_isapnp_list[] __initdata = { {ISAPNP_VENDOR('C','M','I'), ISAPNP_FUNCTION(0x0001), 0, &sb_init_cmi, "CMI 8330 SoundPRO" }, {ISAPNP_VENDOR('R','W','B'), ISAPNP_FUNCTION(0x1688), 0, &sb_init_diamond, "Diamond DT0197H" }, {ISAPNP_VENDOR('A','L','S'), ISAPNP_FUNCTION(0x0001), 0, &sb_init_als, "ALS 100" }, {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), SBF_DEV, &sb_init_ess, "ESS 1688" }, {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), SBF_DEV, &sb_init_ess, "ESS 1868" }, {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), SBF_DEV, &sb_init_ess, "ESS 1868" }, {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), SBF_DEV, &sb_init_ess, "ESS 1869" }, {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), SBF_DEV, &sb_init_ess, "ESS 1878" }, {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), SBF_DEV, &sb_init_ess, "ESS 1879" }, {0} }; static int __init sb_isapnp_init(struct address_info *hw_config, struct address_info *mpu_config, struct pci_bus *bus, struct pci_dev *card, int slot) { struct pci_dev *idev = NULL; /* You missed the init func? That's bad. */ if(sb_isapnp_list[slot].initfunc) { char *busname = bus->name[0] ? bus->name : sb_isapnp_list[slot].name; printk(KERN_INFO "sb: %s detected\n", busname); /* Initialize this baby. */ if((idev = sb_isapnp_list[slot].initfunc(bus, card, hw_config, mpu_config))) { /* We got it. */ printk(KERN_NOTICE "sb: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n", busname, hw_config->io_base, hw_config->irq, hw_config->dma, hw_config->dma2); return 1; } else printk(KERN_INFO "sb: Failed to initialize %s\n", busname); } else printk(KERN_ERR "sb: Bad entry in sb_card.c PnP table\n"); return 0; } /* Actually this routine will detect and configure only the first card with successful initialization. isapnpjump could be used to jump to a specific entry. Please always add entries at the end of the array. Should this be fixed? - azummo */ int __init sb_isapnp_probe(struct address_info *hw_config, struct address_info *mpu_config) { int i; /* Count entries in sb_isapnp_list */ for (i = 0; sb_isapnp_list[i].vendor != 0; i++); /* Check and adjust isapnpjump */ if( isapnpjump < 0 || isapnpjump > ( i - 1 ) ) { printk(KERN_ERR "sb: Valid range for isapnpjump is 0-%d. Adjusted to 0.\n", i-1); isapnpjump = 0; } for (i = isapnpjump; sb_isapnp_list[i].vendor != 0; i++) { if(!(sb_isapnp_list[i].flags & SBF_DEV)) { struct pci_bus *bus = NULL; while ((bus = isapnp_find_card( sb_isapnp_list[i].vendor, sb_isapnp_list[i].function, bus))) { if(sb_isapnp_init(hw_config, mpu_config, bus, NULL, i)) return 0; } } } /* No cards found. I'll try now to search inside every card for a logical device * that matches any entry marked with SBF_DEV in the table. */ for (i = isapnpjump; sb_isapnp_list[i].vendor != 0; i++) { if(sb_isapnp_list[i].flags & SBF_DEV) { struct pci_dev *card = NULL; while ((card = isapnp_find_dev(NULL, sb_isapnp_list[i].vendor, sb_isapnp_list[i].function, card))) { if(sb_isapnp_init(hw_config, mpu_config, card->bus, card, i)) return 0; } } } return -ENODEV; } #endif static int __init init_sb(void) { printk(KERN_INFO "Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); /* Please remember that even with CONFIG_ISAPNP defined one should still be able to disable PNP support for this single driver! */ #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE if(isapnp && (sb_isapnp_probe(&cfg, &cfg_mpu) < 0) ) { printk(KERN_NOTICE "sb_card: No ISAPnP cards found, trying standard ones...\n"); isapnp = 0; } #endif if( isapnp == 0 ) { cfg.io_base = io; cfg.irq = irq; cfg.dma = dma; cfg.dma2 = dma16; } cfg.card_subtype = type; if (!probe_sb(&cfg)) return -ENODEV; attach_sb_card(&cfg); if(cfg.slots[0]==-1) return -ENODEV; if (isapnp == 0) cfg_mpu.io_base = mpu_io; if (probe_sbmpu(&cfg_mpu)) sbmpu = 1; if (sbmpu) attach_sbmpu(&cfg_mpu); return 0; } static void __exit cleanup_sb(void) { if (smw_free) { vfree(smw_free); smw_free = NULL; } unload_sb(&cfg); if (sbmpu) unload_sbmpu(&cfg_mpu); SOUND_LOCK_END; #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE if(activated) { if(sb_dev) sb_dev->deactivate(sb_dev); if(mpu_dev) mpu_dev->deactivate(mpu_dev); } #endif } module_init(init_sb); module_exit(cleanup_sb); #ifndef MODULE static int __init setup_sb(char *str) { /* io, irq, dma, dma2 */ int ints[5]; str = get_options(str, ARRAY_SIZE(ints), ints); io = ints[1]; irq = ints[2]; dma = ints[3]; dma16 = ints[4]; return 1; } __setup("sb=", setup_sb); #endif