At Thu, 12 Apr 2007 21:55:23 +0200, Rask Ingemann Lambertsen wrote: > > From: Rask Ingemann Lambertsen <rask@xxxxxxxxxx> > > - Add code to detect and PnP protocol to manage Media Vision Jazz16 sound > cards. > > Tested with a Jazz16 based sound card and Linux 2.6.20.6. > > Signed-off-by: Rask Ingemann Lambertsen <rask@xxxxxxxxxx> Acked-by: Takashi Iwai <tiwai@xxxxxxx> Adam, could you review it? Thanks, Takashi > --- > > This patch depends on the patch "ALSA: Support Media Vision Jazz16 chips" > because the patched include/sound/sb.h is needed. > > drivers/pnp/Kconfig | 12 > drivers/pnp/Makefile | 1 > drivers/pnp/jazz16pnp.c | 591 ++++++++++++++++++++++++++++++++++++++ > 3 files changed, 604 insertions(+) > > Main changes since the last patch: > Request just one DMA channel (1, 3, 5 or 7). > Request port 0x388-0x38b for the OPL3 alias to avoid conflicts. > Use the recommended resources from the manual for the preferred option. > More SB port bases: 0x210, 0x230 and 0x250. > > diff -rup linux-2.6.20.6-clean/drivers/pnp/Kconfig linux-2.6.20.6-ril/drivers/pnp/Kconfig > --- linux-2.6.20.6-clean/drivers/pnp/Kconfig 2007-02-04 19:44:54.000000000 +0100 > +++ linux-2.6.20.6-ril/drivers/pnp/Kconfig 2007-04-07 20:10:35.000000000 +0200 > @@ -37,5 +37,17 @@ source "drivers/pnp/pnpbios/Kconfig" > > source "drivers/pnp/pnpacpi/Kconfig" > > +# > +# Jazz16 Plug and Play configuration > +# > +config JAZZ16PNP > + bool "Jazz16 sound card Plug and Play support" > + depends on PNP && ISA > + help > + Say Y here if you would like support for automatic configuration > + of I/O ports, DMA channels and IRQ lines of Jazz16 sound cards. > + > + Say Y if you have such a card. Otherwise, say N. > + > endmenu > > diff -rup linux-2.6.20.6-clean/drivers/pnp/Makefile linux-2.6.20.6-ril/drivers/pnp/Makefile > --- linux-2.6.20.6-clean/drivers/pnp/Makefile 2007-02-04 19:44:54.000000000 +0100 > +++ linux-2.6.20.6-ril/drivers/pnp/Makefile 2007-04-07 20:10:35.000000000 +0200 > @@ -7,3 +7,4 @@ obj-y := core.o card.o driver.o resourc > obj-$(CONFIG_PNPACPI) += pnpacpi/ > obj-$(CONFIG_PNPBIOS) += pnpbios/ > obj-$(CONFIG_ISAPNP) += isapnp/ > +obj-$(CONFIG_JAZZ16PNP) += jazz16pnp.o > --- /dev/null 2007-04-11 19:30:17.320748640 +0200 > +++ linux-2.6.20.6-ril/drivers/pnp/jazz16pnp.c 2007-04-11 21:41:39.000000000 +0200 > @@ -0,0 +1,591 @@ > +/* > + * Media Vision Jazz16 Plug & Play support. > + * > + * Copyright (c) 2007 Rask Ingemann Lambertsen <rask@xxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + * > + * This driver manages two devices: > + * > + * Device 0: The game port. > + * Device 1: The SoundBlaster look-alike part. > + * > + * Sources: > + * sound/oss/sb_common.c > + * sys/dev/isa/sbdsp.c (OpenBSD) > + */ > + > +#include <linux/types.h> > +#include <linux/module.h> > +#include <linux/moduleparam.h> > +#include <linux/kernel.h> > +#include <linux/errno.h> > +#include <linux/slab.h> > +#include <linux/init.h> > +#include <linux/pnp.h> > +#include <linux/ioport.h> > +#include <asm/io.h> > +#include <linux/delay.h> > +#include <sound/driver.h> > +#include <sound/core.h> > +#include <sound/sb.h> > + > +MODULE_AUTHOR("Rask Ingemann Lambertsen <rask@xxxxxxxxxx>"); > +MODULE_DESCRIPTION("Jazz16 Plug & Play support"); > +MODULE_LICENSE("GPL"); > + > +int jazz16pnp_disable = false; /* Disable Jazz16 PnP. */ > +module_param_named(disable, jazz16pnp_disable, bool, false); > +MODULE_PARM_DESC(disable, "Jazz16 Plug & Play disable switch"); > + > +int jazz16pnp_verbose = false; /* Verbosity level. */ > +module_param_named(verbose, jazz16pnp_verbose, bool, false); > +MODULE_PARM_DESC(verbose, "Jazz16 Plug & Play verbosity level"); > + > +#define PFX "jazz16pnp: " > +#define PFX_DEBUG KERN_DEBUG PFX > +#define PFX_INFO KERN_INFO PFX > +#define PFX_WARN KERN_WARNING PFX > + > +/* Jazz16 configuration. */ > +#define SB_JAZZ16_CONFIG_PORT 0x201 > +#define SB_JAZZ16_WAKEUP 0xaf > +#define SB_JAZZ16_SET_PORTS 0x50 > +#define SB_JAZZ16_SET_DMAINTR 0xFB > + > +extern void *pnp_alloc(long size); > +extern void pnp_free_option(struct pnp_option *option); > + > +static struct pnp_dev *jazz16_gameport_dev = NULL; > +static struct pnp_dev *jazz16_sb_dev = NULL; > +static struct resource *jazz16pnp_fm_res = NULL; > + > +static uint jazz16_sb_port = 0, jazz16_sb_irq = 0; > +static uint jazz16_sb_dma1 = 0, jazz16_sb_dma2 = 0; > +static uint jazz16_mpu401_port = 0, jazz16_mpu401_irq = 0; > + > +static uint jazz16pnp_count = 0; > + > +static struct pnp_protocol jazz16pnp_protocol; > + > +static bool __init jazz16pnp_easy_option(struct pnp_dev *dev, uint pri, > + struct pnp_port *port, uint nports, > + struct pnp_irq *irq, uint nirqs, > + struct pnp_dma *dma, uint ndmas) > +{ > + struct pnp_option *opt; > + struct pnp_port *new_port; > + struct pnp_irq *new_irq; > + struct pnp_dma *new_dma; > + uint i; > + > + switch (pri) { > + case PNP_RES_PRIORITY_PREFERRED: > + case PNP_RES_PRIORITY_ACCEPTABLE: > + case PNP_RES_PRIORITY_FUNCTIONAL: > + opt = pnp_register_dependent_option(dev, pri); > + break; > + > + default: > + opt = pnp_register_independent_option(dev); > + break; > + } > + if (!opt) > + return false; > + > + for (i = 0; i < nports; i ++) { > + new_port = pnp_alloc(sizeof(*new_port)); > + if (!new_port) > + goto out_no_mem; > + memcpy(new_port, &port[i], sizeof(*new_port)); > + pnp_register_port_resource(opt, new_port); > + } > + for (i = 0; i < nirqs; i ++) { > + new_irq = pnp_alloc(sizeof(*new_irq)); > + if (!new_irq) > + goto out_no_mem; > + memcpy(new_irq, &irq[i], sizeof(*new_irq)); > + pnp_register_irq_resource(opt, new_irq); > + } > + for (i = 0; i < ndmas; i ++) { > + new_dma = pnp_alloc(sizeof(*new_dma)); > + if (!new_dma) > + goto out_no_mem; > + memcpy(new_dma, &dma[i], sizeof(*new_dma)); > + pnp_register_dma_resource(opt, new_dma); > + } > + return true; > + > +out_no_mem: > + return false; > +} > + > +static struct pnp_port jazz16pnp_game_ports[] __initdata = { > + { .min = SB_JAZZ16_CONFIG_PORT, > + .max = SB_JAZZ16_CONFIG_PORT, > + .size = 1, .align = 0, > + .flags = PNP_PORT_FLAG_FIXED, }, > +}; > + > +static int __init jazz16pnp_add_gameport(void) > +{ > + struct pnp_dev *dev; > + struct pnp_id *dev_id; > + > + if (!(dev = pnp_alloc(sizeof(*dev)))) > + goto out_no_dev; > + if (!(dev_id = pnp_alloc(sizeof(*dev_id)))) > + goto out_no_id; > + if (!jazz16pnp_easy_option(dev, PNP_RES_PRIORITY_INVALID, > + jazz16pnp_game_ports, 1, > + NULL, 0, NULL, 0)) > + goto out_no_option; > + > + dev->protocol = &jazz16pnp_protocol; > + dev->capabilities = PNP_READ; > + dev->active = 1; > + dev->number = 0; > + > + pnp_init_resource_table(&dev->res); > + dev->res.port_resource[0].start = SB_JAZZ16_CONFIG_PORT; > + dev->res.port_resource[0].end = SB_JAZZ16_CONFIG_PORT; > + dev->res.port_resource[0].flags = IORESOURCE_IO; > + > + memcpy(dev_id->id, "PNPb02f", PNP_ID_LEN); > + pnp_add_id(dev_id, dev); > + > + jazz16_gameport_dev = dev; > + pnp_add_device(dev); > + return 0; > + > +out_no_option: > + if (dev->independent) > + pnp_free_option(dev->independent); > + kfree(dev_id); > +out_no_id: > + kfree(dev); > +out_no_dev: > + return -ENOMEM; > +}; > + > +static struct pnp_port jazz16pnp_pref_ports[] __initdata = { > + { .min = 0x220, .max = 0x220, .align = 0x00, .size = 0x10 }, > + { .min = 0x330, .max = 0x330, .align = 0x00, .size = 0x02 }, > + { .min = 0x388, .max = 0x388, .align = 0x00, .size = 0x04 }, > +}; > + > +static struct pnp_port jazz16pnp_ports[] __initdata = { > + { .min = 0x210, .max = 0x260, .align = 0x10, .size = 0x10 }, > + { .min = 0x300, .max = 0x330, .align = 0x10, .size = 0x02 }, > + { .min = 0x388, .max = 0x388, .align = 0x00, .size = 0x04 }, > +}; > + > +static struct pnp_irq jazz16pnp_pref_irqs[] __initdata = { > + { .map[0] = (1 << 5), > + .flags = IORESOURCE_IRQ_HIGHEDGE }, > + { .map[0] = (1 << 9), > + .flags = IORESOURCE_IRQ_HIGHEDGE }, > +}; > + > +static struct pnp_irq jazz16pnp_irqs[] __initdata = { > + { .map[0] = (1 << 3) | (1 << 5) | (1 << 7) | > + (1 << 9) | (1 << 10) | (1 << 15), > + .flags = IORESOURCE_IRQ_HIGHEDGE }, > + { .map[0] = (1 << 3) | (1 << 5) | (1 << 7) | > + (1 << 9) | (1 << 10) | (1 << 15), > + .flags = IORESOURCE_IRQ_HIGHEDGE }, > +}; > + > +static struct pnp_dma jazz16pnp_pref_dmas[] __initdata = { > + { .map = (1 << 1), .flags = IORESOURCE_DMA_8BIT, }, > +}; > + > +static struct pnp_dma jazz16pnp_dmas[] __initdata = { > + { .map = (1 << 1) | (1 << 3) | (1 << 5) | (1 << 7), > + .flags = IORESOURCE_DMA_8AND16BIT, }, > +}; > + > +static int __init jazz16pnp_add_sb(void) > +{ > + struct pnp_dev *sbdev; > + struct pnp_id *sbid; > + > + if (!(sbdev = pnp_alloc(sizeof(*sbdev)))) > + goto out_no_sbdev; > + if (!(sbid = pnp_alloc(sizeof(*sbid)))) > + goto out_no_sbid; > + > + if (!jazz16pnp_easy_option(sbdev, PNP_RES_PRIORITY_PREFERRED, > + jazz16pnp_pref_ports, 3, > + jazz16pnp_pref_irqs, 2, > + jazz16pnp_pref_dmas, 1)) > + goto out_no_sboption; > + if (!jazz16pnp_easy_option(sbdev, PNP_RES_PRIORITY_ACCEPTABLE, > + jazz16pnp_ports, 3, > + jazz16pnp_irqs, 2, > + jazz16pnp_dmas, 1)) > + goto out_no_sboption; > + if (!jazz16pnp_easy_option(sbdev, PNP_RES_PRIORITY_ACCEPTABLE, > + jazz16pnp_ports, 3, > + jazz16pnp_irqs, 1, > + jazz16pnp_dmas, 1)) > + goto out_no_sboption; > + memcpy(sbid->id, "PNPb00f", PNP_ID_LEN); > + pnp_add_id(sbid, sbdev); > + > + pnp_init_resource_table(&sbdev->res); > + sbdev->capabilities = PNP_READ | PNP_WRITE | PNP_CONFIGURABLE | PNP_DISABLE; > + sbdev->protocol = &jazz16pnp_protocol; > + sbdev->number = 1; > + > + jazz16_sb_dev = sbdev; > + pnp_add_device(sbdev); > + return 0; > + > +out_no_sboption: > + if (sbdev->dependent) > + pnp_free_option(sbdev->dependent); > + kfree(sbid); > +out_no_sbid: > + kfree(sbdev); > +out_no_sbdev: > + return -ENOMEM; > +} > + > +/* Configure the SB and MPU ports. Passing 0 for sbport disables the Jazz16. */ > +static void jazz16_port_setup(const uint sbport, const uint mpuport) > +{ > + u8 config; > + > + switch (sbport) { > + case 0x210: > + case 0x220: > + case 0x230: > + case 0x240: > + case 0x250: > + case 0x260: > + config = sbport & 0x70; > + jazz16_sb_port = sbport; > + break; > + > + default: > + config = 0; > + jazz16_sb_port = 0; > + break; > + } > + > + switch (mpuport) { > + case 0x300: > + case 0x310: > + case 0x320: > + case 0x330: > + config |= (mpuport >> 4) & 0x07; > + jazz16_mpu401_port = mpuport; > + break; > + > + default: > + if (jazz16_sb_port) > + BUG(); > + } > + if (jazz16pnp_verbose) > + printk(PFX_DEBUG "Setting SB port = %#x, MPU port = %#x.\n", > + jazz16_sb_port, jazz16_mpu401_port); > + outb(SB_JAZZ16_WAKEUP, SB_JAZZ16_CONFIG_PORT); > + outb(SB_JAZZ16_SET_PORTS, SB_JAZZ16_CONFIG_PORT); > + outb(config, SB_JAZZ16_CONFIG_PORT); > +} > + > +static u8 jazz16_irq_config(uint irq) > +{ > + switch (irq) { > + case 3: return 3; > + case 5: return 1; > + case 7: return 4; > + case 9: return 2; > + case 10: return 5; > + case 15: return 6; > + default: return 0; > + } > +} > + > +static u8 jazz16_dma_config(uint dma) > +{ > + switch (dma) { > + case 1: return 1; > + case 3: return 2; > + case 5: return 3; > + case 7: return 4; > + default: return 0; > + } > +} > + > +/* The my_sbdsp_xxx functions were ripped from sound/isa/sb/sb_common.c. */ > +#define BUSY_LOOPS 100000 > + > +static int my_sbdsp_command(unsigned int base, unsigned char val) > +{ > + int i; > + for (i = BUSY_LOOPS; i; i--) > + if ((inb(SBP1(base, STATUS)) & 0x80) == 0) { > + outb(val, SBP1(base, COMMAND)); > + return 1; > + } > + return 0; > +} > + > +static int my_sbdsp_get_byte(unsigned int base) > +{ > + int val; > + int i; > + for (i = BUSY_LOOPS; i; i--) { > + if (inb(SBP1(base, DATA_AVAIL)) & 0x80) { > + val = inb(SBP1(base, READ)); > + return val; > + } > + } > + return -ENODEV; > +} > + > +static int my_sbdsp_reset(unsigned int base) > +{ > + int i; > + > + outb(1, SBP1(base, RESET)); > + udelay(10); > + outb(0, SBP1(base, RESET)); > + udelay(30); > + for (i = BUSY_LOOPS; i; i--) > + if (inb(SBP1(base, DATA_AVAIL)) & 0x80) { > + if (inb(SBP1(base, READ)) == 0xaa) > + return 0; > + else > + break; > + } > + return -ENODEV; > +} > + > +static int jazz16_irq_dma_setup(unsigned int base, uint sbirq, uint mpuirq, uint dma8, uint dma16) > +{ > + u8 dma_config, irq_config; > + > + if (jazz16pnp_verbose) > + printk(PFX_DEBUG "Setting SB irq %u, dma %u&%u, MPU irq %u.\n", > + sbirq, dma8, dma16, mpuirq); > + irq_config = jazz16_irq_config(sbirq) | jazz16_irq_config(mpuirq) << 4; > + dma_config = jazz16_dma_config(dma8) | jazz16_dma_config(dma16) << 4; > + if (!my_sbdsp_command(base, SB_JAZZ16_SET_DMAINTR)) > + return 0; > + > + if (!my_sbdsp_command(base, dma_config)) > + return 0; > + jazz16_sb_dma1 = dma8; > + jazz16_sb_dma2 = dma16; > + > + if (!my_sbdsp_command(base, irq_config)) > + return 0; > + jazz16_sb_irq = sbirq; > + jazz16_mpu401_irq = mpuirq; > + return 1; > +} > + > +static int jazz16pnp_get_resources(struct pnp_dev *dev, struct pnp_resource_table *res) > +{ > + pnp_init_resource_table(res); > + if (dev == jazz16_gameport_dev) { > + res->port_resource[0].start = res->port_resource[0].end = SB_JAZZ16_CONFIG_PORT; > + res->port_resource[0].flags = IORESOURCE_IO; > + } else if (dev == jazz16_sb_dev) { > + if (!jazz16_sb_port) > + return 0; > + res->port_resource[0].start = jazz16_sb_port; > + res->port_resource[0].end = jazz16_sb_port + 0x10 - 1; > + res->port_resource[0].flags = IORESOURCE_IO; > + > + res->irq_resource[0].start = jazz16_sb_irq; > + res->irq_resource[0].end = jazz16_sb_irq; > + res->irq_resource[0].flags = IORESOURCE_IRQ; > + if (jazz16_mpu401_irq) { > + res->irq_resource[1].start = jazz16_mpu401_irq; > + res->irq_resource[1].end = jazz16_mpu401_irq; > + res->irq_resource[1].flags = IORESOURCE_IRQ; > + } > + > + res->dma_resource[0].start = jazz16_sb_dma1; > + res->dma_resource[0].end = jazz16_sb_dma1; > + res->dma_resource[0].flags = IORESOURCE_DMA; > + if (jazz16_sb_dma2 != jazz16_sb_dma1) { > + res->dma_resource[1].start = jazz16_sb_dma2; > + res->dma_resource[1].end = jazz16_sb_dma2; > + res->dma_resource[1].flags = IORESOURCE_DMA; > + } > + res->port_resource[1].start = jazz16_mpu401_port; > + res->port_resource[1].end = jazz16_mpu401_port + 2 - 1; > + res->port_resource[1].flags = IORESOURCE_IO; > + } else > + return -EINVAL; > + return 0; > +} > + > +/* Disables the device if supported. */ > +static int jazz16pnp_disable_resources(struct pnp_dev *dev) > +{ > + if (dev == jazz16_gameport_dev) { > + return -ENOSYS; > + } else if (dev == jazz16_sb_dev) { > + if (jazz16_sb_port) > + jazz16_irq_dma_setup(jazz16_sb_port, 0, 0, 0, 0); > + jazz16_port_setup(0, 0); > + if (jazz16pnp_fm_res) { > + release_resource(jazz16pnp_fm_res); > + jazz16pnp_fm_res = NULL; > + } > + dev->active = false; > + return 0; > + } else { > + return -EINVAL; > + } > +} > + > +#define PNP_RES_IS_SET(res,type,TYPE,i) \ > + (((res)->type##_resource[i].flags & (IORESOURCE_##TYPE | IORESOURCE_UNSET)) \ > + == IORESOURCE_##TYPE) > + > +static int jazz16pnp_set_resources(struct pnp_dev *dev, struct pnp_resource_table *res) > +{ > + unsigned int sbirq, mpuirq, dma1, dma2; > + > + if (dev == jazz16_gameport_dev) > + return -ENOSYS; > + > + else if (dev == jazz16_sb_dev) { > + if (PNP_RES_IS_SET(res, dma, DMA, 0)) > + dma1 = res->dma_resource[0].start; > + else > + return -EINVAL; > + if (PNP_RES_IS_SET(res, dma, DMA, 1)) > + dma2 = res->dma_resource[1].start; > + else > + dma2 = dma1; /* "half duplex" operation. */ > + if (PNP_RES_IS_SET(res, irq, IRQ, 0)) > + sbirq = res->irq_resource[0].start; > + else > + return -EINVAL; > + if (PNP_RES_IS_SET(res, irq, IRQ, 1)) > + mpuirq = res->irq_resource[1].start; > + else > + mpuirq = 0; /* MPU-401 driver supports this. */ > + if (! PNP_RES_IS_SET(res, port, IO, 0)) > + return -EINVAL; > + if (! PNP_RES_IS_SET(res, port, IO, 1)) > + return -EINVAL; > + > + /* Block the OPL3 alias to avoid conflicts. */ > + jazz16pnp_fm_res = request_region(0x388, 4, "SoundBlaster FM"); > + /* Avoid setting the mpu port after setting the sb port > + because doing so will glitch the mpu port from the > + previous one to the new one. */ > + jazz16_port_setup(res->port_resource[0].start, > + res->port_resource[1].start); > + jazz16_irq_dma_setup(jazz16_sb_port, sbirq, mpuirq, dma1, dma2); > + dev->active = true; > + return 0; > + } else > + return -EINVAL; > +} > +#undef PNP_RES_IS_SET > + > +static struct pnp_protocol jazz16pnp_protocol = { > + .name = "Jazz16 PnP", > + .get = jazz16pnp_get_resources, > + .set = jazz16pnp_set_resources, > + .disable = jazz16pnp_disable_resources, > +}; > + > +static uint __init jazz16_check(unsigned int base) > +{ > + if (my_sbdsp_reset(base)) > + return 0; > + if (!(my_sbdsp_command(base, SB_DSP_GET_JAZZ_VERSION))) > + return 0; > + return SB_VERSION_IS_JAZZ16(my_sbdsp_get_byte(base)); > +} > + > +static int __init jazz16pnp_init(void) > +{ > + int err = 0; > + unsigned int base, mpubase; > + > + if (jazz16pnp_disable == 1) { > + printk(PFX_INFO "Jazz16 Plug & Play support disabled.\n"); > + return 0; > + } > + if (!request_region(SB_JAZZ16_CONFIG_PORT, 1, "Jazz16 PnP probe")) > + return -EBUSY; > + > + for (mpubase = 0x300; mpubase <= 0x330; mpubase += 0x10) > + if (request_region(mpubase, 2, "Jazz16 PnP probe")) > + break; > + if (mpubase > 0x330) > + goto out_no_mpubase; > + > + if (jazz16pnp_verbose) > + printk(PFX_DEBUG "Scanning for Jazz16 cards...\n"); > + > + for (base = 0x260; base >= 0x210; base -= 0x10) { > + if (!request_region(base, 16, "Jazz16 PnP probe")) > + continue; > + if (jazz16pnp_verbose) > + printk(PFX_DEBUG "Probing at %#x (with MPU at %#x).\n", > + base, mpubase); > + jazz16_port_setup(base, mpubase); > + if (jazz16_check(base)) > + jazz16pnp_count++; > + jazz16_irq_dma_setup(base, 0, 0, 0, 0); > + jazz16_port_setup(0, 0); > + release_region(base, 16); > + if (jazz16pnp_count) > + break; > + } > + if (base < 0x210) > + goto out_no_sbbase; > + if (!jazz16pnp_count) { > + err = -ENODEV; > + goto out_no_devices; > + } > + if (pnp_register_protocol(&jazz16pnp_protocol) < 0) > + err = -ENOMEM; > + else { > + if (jazz16pnp_add_gameport()) > + printk(PFX_WARN "Failed to add game port.\n"); > + if (jazz16pnp_add_sb()) > + printk(PFX_WARN "Failed to add SB port.\n"); > + err = 0; > + } > +out_no_devices: > + printk(PFX_INFO "%u Jazz16 card%s detected.\n", jazz16pnp_count, > + jazz16pnp_count == 1 ? "" : "s"); > +out_no_sbbase: > + release_region(mpubase, 2); > +out_no_mpubase: > + release_region(SB_JAZZ16_CONFIG_PORT, 1); > + return err; > +} > +module_init(jazz16pnp_init); > + > +static void __exit jazz16pnp_exit(void) > +{ > + pnp_unregister_protocol(&jazz16pnp_protocol); > +} > +module_exit(jazz16pnp_exit); > > > -- > Rask Ingemann Lambertsen > _______________________________________________ > Alsa-devel mailing list > Alsa-devel@xxxxxxxxxxxxxxxx > http://mailman.alsa-project.org/mailman/listinfo/alsa-devel > _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel