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 configurationof 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;elsebreak;}- 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;elsereturn -EINVAL;if (PNP_RES_IS_SET(res, dma, DMA, 1))dma2 = res->dma_resource[1].start;elsedma2 = dma1; /* "half duplex" operation. */if (PNP_RES_IS_SET(res, irq, IRQ, 0))sbirq = res->irq_resource[0].start;elsereturn -EINVAL;if (PNP_RES_IS_SET(res, irq, IRQ, 1))mpuirq = res->irq_resource[1].start;elsempuirq = 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 portbecause doing so will glitch the mpu port from theprevious 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