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

Rask Ingemann Lambertsen rask at sygehus.dk
Mon Mar 26 23:34:54 CEST 2007


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.

Signed-off-by: Rask Ingemann Lambertsen <rask at sygehus.dk>
---

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 |  555 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 568 insertions(+)

--- linux-2.6.20/drivers/pnp/Makefile-orig	2007-02-19 23:24:22.000000000 +0100
+++ linux-2.6.20/drivers/pnp/Makefile	2007-02-19 23:24:22.000000000 +0100
@@ -8,3 +8,4 @@ obj-$(CONFIG_PNPACPI)		+= pnpacpi/
 obj-$(CONFIG_PNPACPI)		+= pnpacpi/
 obj-$(CONFIG_PNPBIOS)		+= pnpbios/
 obj-$(CONFIG_ISAPNP)		+= isapnp/
+obj-$(CONFIG_JAZZ16PNP)		+= jazz16pnp.o
--- linux-2.6.20/drivers/pnp/jazz16pnp.c-orig	2007-03-11 22:11:58.000000000 +0100
+++ linux-2.6.20/drivers/pnp/jazz16pnp.c	2007-03-25 21:27:18.000000000 +0200
@@ -0,0 +1,555 @@
+/*
+ *  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[1] __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_ports[2] __initdata = {
+	{ .min = 0x220, .max = 0x260, .align = 0x20, .size = 0x10 },
+	{ .min = 0x300, .max = 0x330, .align = 0x10, .size = 0x02 },
+};
+
+static struct pnp_irq jazz16pnp_irqs[2] __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_dmas[2] __initdata = {
+	{ .map = (1 << 1) | (1 << 3), .flags = IORESOURCE_DMA_8BIT, },
+	{ .map = (1 << 5) | (1 << 7), .flags = IORESOURCE_DMA_16BIT, },
+};
+
+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_ports, 2,
+				   jazz16pnp_irqs, 2,
+				   jazz16pnp_dmas, 2))
+		goto out_no_sboption;
+	if (!jazz16pnp_easy_option(sbdev, PNP_RES_PRIORITY_ACCEPTABLE,
+				   jazz16pnp_ports, 2,
+				   jazz16pnp_irqs, 1,
+				   jazz16pnp_dmas, 2))
+		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 0x220:
+	case 0x240:
+	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 = 0x%x, MPU port = 0x%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 = res->irq_resource[0].end = jazz16_sb_irq;
+		res->irq_resource[0].flags = IORESOURCE_IRQ;
+
+		res->dma_resource[0].start = res->dma_resource[0].end = jazz16_sb_dma1;
+		res->dma_resource[0].flags = IORESOURCE_DMA;
+		res->dma_resource[1].start = 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;
+
+		if (!jazz16_mpu401_irq)
+			return 0;
+		res->irq_resource[1].start = res->irq_resource[0].end = jazz16_mpu401_irq;
+		res->irq_resource[1].flags = IORESOURCE_IRQ;
+	}
+	else
+		return -EINVAL;
+	return 0;
+}
+
+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 ((res->port_resource[0].flags & (IORESOURCE_IO | IORESOURCE_UNSET)) == IORESOURCE_IO)
+			jazz16_port_setup(res->port_resource[0].start, jazz16_mpu401_port);
+		if ((res->port_resource[1].flags & (IORESOURCE_IO | IORESOURCE_UNSET)) == IORESOURCE_IO)
+			jazz16_port_setup(jazz16_sb_port, res->port_resource[1].start);
+		if ((res->dma_resource[0].flags & (IORESOURCE_DMA | IORESOURCE_UNSET)) == IORESOURCE_DMA)
+			dma1 = res->dma_resource[0].start;
+		else
+			dma1 = jazz16_sb_dma1;
+		if ((res->dma_resource[1].flags & (IORESOURCE_DMA | IORESOURCE_UNSET)) == IORESOURCE_DMA)
+			dma2 = res->dma_resource[1].start;
+		else
+			dma2 = jazz16_sb_dma2;
+		if ((res->irq_resource[0].flags & (IORESOURCE_IRQ | IORESOURCE_UNSET)) == IORESOURCE_IRQ)
+			sbirq = res->irq_resource[0].start;
+		else
+			sbirq = jazz16_sb_irq;
+		if ((res->irq_resource[1].flags & (IORESOURCE_IRQ | IORESOURCE_UNSET)) == IORESOURCE_IRQ)
+			mpuirq = res->irq_resource[1].start;
+		else
+			mpuirq = jazz16_mpu401_irq;
+		if (jazz16_sb_port) {
+			/* Block the OPL3 alias to avoid conflicts. */
+			jazz16pnp_fm_res = request_region(0x388, 4, "SoundBlaster FM");
+			jazz16_irq_dma_setup(jazz16_sb_port, sbirq, mpuirq, dma1, dma2);
+		} else
+			return -EBUSY;
+		dev->active = true;
+		return 0;
+	}
+	else
+		return -EINVAL;
+}
+
+static int jazz16pnp_disable_resources(struct pnp_dev *dev)
+{
+	if (dev == jazz16_gameport_dev) {
+		return -ENOSYS;
+	}
+	else if (dev == jazz16_sb_dev) {
+		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;
+	}
+}
+
+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 = 0x330; mpubase >= 0x300; mpubase -= 0x10)
+		if (request_region(mpubase, 2, "Jazz16 PnP probe"))
+			break;
+	if (mpubase < 0x300)
+		goto out_no_mpubase;
+
+	if (jazz16pnp_verbose)
+		printk(PFX_DEBUG "Scanning for Jazz16 cards...\n");
+
+	for (base = 0x260; base >= 0x220; base -= 0x20) {
+		if (!request_region(base, 16, "Jazz16 PnP probe"))
+			continue;
+		if (jazz16pnp_verbose)
+			printk(PFX_DEBUG "Probing at 0x%x.\n", base);
+		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 < 0x220)
+		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);
--- linux-2.6.20/drivers/pnp/Kconfig-orig	2007-02-19 23:23:26.000000000 +0100
+++ linux-2.6.20/drivers/pnp/Kconfig	2007-03-25 21:44:48.000000000 +0200
@@ -39,5 +39,17 @@ source "drivers/pnp/pnpacpi/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
 


-- 
Rask Ingemann Lambertsen


More information about the Alsa-devel mailing list