At Thu, 12 Apr 2007 21:55:23 +0200, Rask Ingemann Lambertsen wrote:
From: Rask Ingemann Lambertsen rask@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@sygehus.dk
Acked-by: Takashi Iwai tiwai@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@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@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@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel