[alsa-devel] [PATCH] PnP: Protocol for Media Vision Jazz16 sound cards

Takashi Iwai tiwai at suse.de
Fri Apr 13 16:05:11 CEST 2007


At Thu, 12 Apr 2007 21:55:23 +0200,
Rask Ingemann Lambertsen wrote:
> 
> From: Rask Ingemann Lambertsen <rask at sygehus.dk>
> 
>  - 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 at sygehus.dk>

Acked-by: Takashi Iwai <tiwai at suse.de>

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 at sygehus.dk>
> + *
> + *   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 at sygehus.dk>");
> +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 at alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
> 


More information about the Alsa-devel mailing list