From: Rask Ingemann Lambertsen rask@sygehus.dk
- Add support for Jazz16 features to the common SoundBlaster code. - Add a front end for Jazz16 based sound cards.
Tested with a Jazz16 based sound card and Linux 2.6.20.6.
Signed-off-by: Rask Ingemann Lambertsen rask@sygehus.dk ---
This patch is a prerequisite for the Jazz PnP patch, which needs the patched include/sound/sb.h.
Documentation/sound/alsa/ALSA-Configuration.txt | 10 include/sound/sb.h | 5 sound/isa/Kconfig | 15 + sound/isa/sb/Makefile | 2 sound/isa/sb/jazz16.c | 183 ++++++++++++++ sound/isa/sb/sb8_main.c | 98 +++++++ sound/isa/sb/sb_common.c | 12 sound/isa/sb/sb_mixer.c | 3 8 files changed, 323 insertions(+), 5 deletions(-)
Changes since the last patch: The same DMA channel is now used for both 8-bit and 16-bit sound. DSP high-speed mode isn't used at all now for the Jazz16. A 16-bit DMA buffer can now be 128 kB instead of 64 kB.
diff -rup linux-2.6.20.6-clean/Documentation/sound/alsa/ALSA-Configuration.txt linux-2.6.20.6-ril/Documentation/sound/alsa/ALSA-Configuration.txt --- linux-2.6.20.6-clean/Documentation/sound/alsa/ALSA-Configuration.txt 2007-02-04 19:44:54.000000000 +0100 +++ linux-2.6.20.6-ril/Documentation/sound/alsa/ALSA-Configuration.txt 2007-04-07 17:31:38.000000000 +0200 @@ -1120,6 +1120,16 @@ Prior to version 0.9.0rc4 options had a
This module supports multiple cards, autoprobe and ISA PnP.
+ Module snd-jazz16 + ----------------- + + Module for sound cards based on Media Vision Jazz16 chip (PnP only) + + This module supports multiple cards and PnP. + + Note: You will most likely want to enable Jazz16 PnP support + (CONFIG_JAZZ16PNP) in the kernel. + Module snd-korg1212 -------------------
diff -rup linux-2.6.20.6-clean/include/sound/sb.h linux-2.6.20.6-ril/include/sound/sb.h --- linux-2.6.20.6-clean/include/sound/sb.h 2007-02-04 19:44:54.000000000 +0100 +++ linux-2.6.20.6-ril/include/sound/sb.h 2007-04-07 17:31:38.000000000 +0200 @@ -33,6 +33,7 @@ enum sb_hw_type { SB_HW_20, SB_HW_201, SB_HW_PRO, + SB_HW_JAZZ16, /* Media Vision Jazz16 chip */ SB_HW_16, SB_HW_16CSP, /* SB16 with CSP chip */ SB_HW_ALS100, /* Avance Logic ALS100 chip */ @@ -142,6 +143,7 @@ struct snd_sb { #define SB_DSP_HI_INPUT_AUTO 0x98 #define SB_DSP_IMMED_INT 0xf2 #define SB_DSP_GET_VERSION 0xe1 +#define SB_DSP_GET_JAZZ_VERSION 0xfa #define SB_DSP_SPEAKER_ON 0xd1 #define SB_DSP_SPEAKER_OFF 0xd3 #define SB_DSP_DMA8_OFF 0xd0 @@ -265,6 +267,9 @@ struct snd_sb { #define SB_DMASETUP_DMA6 0x40 #define SB_DMASETUP_DMA7 0x80
+/* Check the output of SB_DSP_GET_JAZZ_VERSION. */ +#define SB_VERSION_IS_JAZZ16(x) ((x) == 0x12) + /* * */ diff -rup linux-2.6.20.6-clean/sound/isa/Kconfig linux-2.6.20.6-ril/sound/isa/Kconfig --- linux-2.6.20.6-clean/sound/isa/Kconfig 2007-02-04 19:44:54.000000000 +0100 +++ linux-2.6.20.6-ril/sound/isa/Kconfig 2007-04-07 17:31:38.000000000 +0200 @@ -248,6 +248,21 @@ config SND_INTERWAVE_STB To compile this driver as a module, choose M here: the module will be called snd-interwave-stb.
+config SND_JAZZ16 + tristate "Media Vision Jazz16" + depends on SND + select SND_OPL3_LIB + select SND_PCM + help + Say Y here to include support for Media Vision Jazz16 based + soundcards. + + You probably want to enable Jazz16 Plug and Play support + (CONFIG_JAZZ16PNP) also. + + To compile this driver as a module, choose M here: the module + will be called snd-jazz16. + config SND_OPL3SA2 tristate "Yamaha OPL3-SA2/SA3" depends on SND diff -rup linux-2.6.20.6-clean/sound/isa/sb/Makefile linux-2.6.20.6-ril/sound/isa/sb/Makefile --- linux-2.6.20.6-clean/sound/isa/sb/Makefile 2007-02-04 19:44:54.000000000 +0100 +++ linux-2.6.20.6-ril/sound/isa/sb/Makefile 2007-04-07 17:31:38.000000000 +0200 @@ -12,6 +12,7 @@ snd-sb16-objs := sb16.o snd-sbawe-objs := sbawe.o emu8000.o snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o snd-es968-objs := es968.o +snd-jazz16-objs := jazz16.o
# # this function returns: @@ -30,6 +31,7 @@ obj-$(CONFIG_SND_SB16) += snd-sb16.o snd obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o snd-sb16-dsp.o snd-sb-common.o obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o +obj-$(CONFIG_SND_JAZZ16) += snd-jazz16.o snd-sb8-dsp.o snd-sb-common.o ifeq ($(CONFIG_SND_SB16_CSP),y) obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o diff -rup linux-2.6.20.6-clean/sound/isa/sb/sb8_main.c linux-2.6.20.6-ril/sound/isa/sb/sb8_main.c --- linux-2.6.20.6-clean/sound/isa/sb/sb8_main.c 2007-02-04 19:44:54.000000000 +0100 +++ linux-2.6.20.6-ril/sound/isa/sb/sb8_main.c 2007-04-12 00:03:35.000000000 +0200 @@ -28,6 +28,9 @@ * * Wed Jul 12 22:02:55 CEST 2000 Uros Bizjak uros@kss-loka.si * Cleaned up and rewrote lowlevel routines. + * + * Wed Apr 11 23:56:18 CEST 2007 Rask Ingemann Lambertsen rask@sygehus.dk + * Added Jazz16 enhancements (mostly ported from sound/oss/sb_common.c). */
#include <sound/driver.h> @@ -73,6 +76,15 @@ static struct snd_ratnum stereo_clocks[] } };
+/* For stereo playback and capture, the denominator is divided by two, so it + * must be even to get the intended sample rate. */ +static struct snd_ratnum jazz16_stereo_clock = { + .num = SB8_CLOCK, + .den_min = 2, + .den_max = 512, + .den_step = 2, +}; + static int snd_sb8_hw_constraint_rate_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -101,13 +113,52 @@ static int snd_sb8_hw_constraint_channel return 0; }
+static int snd_jazz16_hw_constraint_rate_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + int err = 0; + + if (c->max > 1) { + unsigned int num = 0, den = 0; + err = snd_interval_ratnum(r, 1, &jazz16_stereo_clock, &num, &den); + if (err >= 0 && den) { + params->rate_num = num; + params->rate_den = den; + } + } + return err; +} + +static int snd_jazz16_hw_constraint_channels_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + int err = 0; + + /* Force mono mode if the sample rate interval doesn't allow stereo. */ + if (SB8_DEN(r->min) == SB8_DEN(r->max) + && SB8_DEN(r->min) & 1) + { + struct snd_interval t = { .min = 1, .max = 1 }; + struct snd_interval *c; + + c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + err = snd_interval_refine(c, &t); + } + return err; +} + static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream) { unsigned long flags; struct snd_sb *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; unsigned int mixreg, rate, size, count; + unsigned char stereo, format;
+ stereo = runtime->channels > 1; rate = runtime->rate; switch (chip->hardware) { case SB_HW_PRO: @@ -123,6 +174,7 @@ static int snd_sb8_playback_prepare(stru break; } /* fallthru */ + case SB_HW_JAZZ16: case SB_HW_20: chip->playback_format = SB_DSP_LO_OUTPUT_AUTO; break; @@ -134,8 +186,14 @@ static int snd_sb8_playback_prepare(stru } size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); count = chip->p_period_size = snd_pcm_lib_period_bytes(substream); + if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) + format = stereo ? SB_DSP_STEREO_16BIT : SB_DSP_MONO_16BIT; + else + format = stereo ? SB_DSP_STEREO_8BIT : SB_DSP_MONO_8BIT; spin_lock_irqsave(&chip->reg_lock, flags); snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); + if (chip->hardware == SB_HW_JAZZ16) + snd_sbdsp_command(chip, format); if (runtime->channels > 1) { /* set playback stereo mode */ spin_lock(&chip->mixer_lock); @@ -167,6 +225,8 @@ static int snd_sb8_playback_prepare(stru snd_sbdsp_command(chip, 256 - runtime->rate_den); } if (chip->playback_format != SB_DSP_OUTPUT) { + if (chip->dma8 > 3) + count /= 2; count--; snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); snd_sbdsp_command(chip, count & 0xff); @@ -233,7 +293,9 @@ static int snd_sb8_capture_prepare(struc struct snd_sb *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; unsigned int mixreg, rate, size, count; + unsigned char stereo, format;
+ stereo = runtime->channels > 1; rate = runtime->rate; switch (chip->hardware) { case SB_HW_PRO: @@ -250,6 +312,7 @@ static int snd_sb8_capture_prepare(struc break; } /* fallthru */ + case SB_HW_JAZZ16: case SB_HW_20: chip->capture_format = SB_DSP_LO_INPUT_AUTO; break; @@ -261,10 +324,14 @@ static int snd_sb8_capture_prepare(struc } size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream); count = chip->c_period_size = snd_pcm_lib_period_bytes(substream); + if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) + format = stereo ? SB_DSP_STEREO_16BIT : SB_DSP_MONO_16BIT; + else + format = stereo ? SB_DSP_STEREO_8BIT : SB_DSP_MONO_8BIT; spin_lock_irqsave(&chip->reg_lock, flags); snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); - if (runtime->channels > 1) - snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT); + if (stereo || chip->hardware == SB_HW_JAZZ16) + snd_sbdsp_command(chip, format); snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE); if (runtime->channels > 1) { snd_sbdsp_command(chip, 256 - runtime->rate_den / 2); @@ -279,6 +346,8 @@ static int snd_sb8_capture_prepare(struc snd_sbdsp_command(chip, 256 - runtime->rate_den); } if (chip->capture_format != SB_DSP_OUTPUT) { + if (chip->dma8 > 3) + count /= 2; count--; snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); snd_sbdsp_command(chip, count & 0xff); @@ -443,6 +512,19 @@ static int snd_sb8_open(struct snd_pcm_s runtime->hw = snd_sb8_capture; } switch (chip->hardware) { + case SB_HW_JAZZ16: + runtime->hw.formats |= SNDRV_PCM_FMTBIT_S16_LE; + runtime->hw.rates |= SNDRV_PCM_RATE_8000_44100; + runtime->hw.rate_max = 45455; + runtime->hw.channels_max = 2; + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + snd_jazz16_hw_constraint_rate_channels, NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_RATE, -1); + snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + snd_jazz16_hw_constraint_channels_rate, NULL, + SNDRV_PCM_HW_PARAM_RATE, -1); + break; case SB_HW_PRO: runtime->hw.rate_max = 44100; runtime->hw.channels_max = 2; @@ -465,6 +547,14 @@ static int snd_sb8_open(struct snd_pcm_s } snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_clock); + if (chip->dma8 > 3) { + snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 2); + snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 2); + runtime->hw.buffer_bytes_max = 128 * 1024 * 1024; + runtime->hw.period_bytes_max = 128 * 1024 * 1024; + } return 0; }
@@ -512,6 +602,7 @@ int snd_sb8dsp_pcm(struct snd_sb *chip, struct snd_card *card = chip->card; struct snd_pcm *pcm; int err; + size_t max_prealloc;
if (rpcm) *rpcm = NULL; @@ -524,9 +615,10 @@ int snd_sb8dsp_pcm(struct snd_sb *chip, snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops);
+ max_prealloc = (chip->dma8 > 3) ? 128 * 1024 : 64 * 1024; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(), - 64*1024, 64*1024); + 64*1024, max_prealloc);
if (rpcm) *rpcm = pcm; diff -rup linux-2.6.20.6-clean/sound/isa/sb/sb_common.c linux-2.6.20.6-ril/sound/isa/sb/sb_common.c --- linux-2.6.20.6-clean/sound/isa/sb/sb_common.c 2007-02-04 19:44:54.000000000 +0100 +++ linux-2.6.20.6-ril/sound/isa/sb/sb_common.c 2007-04-11 20:23:55.000000000 +0200 @@ -146,8 +146,16 @@ static int snd_sbdsp_probe(struct snd_sb } break; case 3: - chip->hardware = SB_HW_PRO; - str = "Pro"; + spin_lock_irqsave(&chip->reg_lock, flags); + if (snd_sbdsp_command(chip, SB_DSP_GET_JAZZ_VERSION) && + SB_VERSION_IS_JAZZ16(snd_sbdsp_get_byte (chip))) { + chip->hardware = SB_HW_JAZZ16; + str = "Pro (Jazz16)"; + } else { + chip->hardware = SB_HW_PRO; + str = "Pro"; + } + spin_unlock_irqrestore (&chip->reg_lock, flags); break; case 4: chip->hardware = SB_HW_16; diff -rup linux-2.6.20.6-clean/sound/isa/sb/sb_mixer.c linux-2.6.20.6-ril/sound/isa/sb/sb_mixer.c --- linux-2.6.20.6-clean/sound/isa/sb/sb_mixer.c 2007-02-04 19:44:54.000000000 +0100 +++ linux-2.6.20.6-ril/sound/isa/sb/sb_mixer.c 2007-04-07 17:31:38.000000000 +0200 @@ -811,6 +811,7 @@ int snd_sbmixer_new(struct snd_sb *chip) return err; break; case SB_HW_PRO: + case SB_HW_JAZZ16: if ((err = snd_sbmixer_init(chip, snd_sbpro_controls, ARRAY_SIZE(snd_sbpro_controls), @@ -946,6 +947,7 @@ void snd_sbmixer_suspend(struct snd_sb * save_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs)); break; case SB_HW_PRO: + case SB_HW_JAZZ16: save_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs)); break; case SB_HW_16: @@ -971,6 +973,7 @@ void snd_sbmixer_resume(struct snd_sb *c restore_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs)); break; case SB_HW_PRO: + case SB_HW_JAZZ16: restore_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs)); break; case SB_HW_16: --- /dev/null 2007-04-11 19:30:17.320748640 +0200 +++ linux-2.6.20.6-ril/sound/isa/sb/jazz16.c 2007-04-05 23:30:27.000000000 +0200 @@ -0,0 +1,183 @@ +/* Driver for Media Vision Jazz16 based boards. + * + * Copyright (c) 2007 by Rask Ingemann Lambertsen + * + * 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 + * + * The Jazz16 is an SB Pro compatible chip with a 16-bit mode and higher + * playback and capture rates added to it. It is not SB 16 compatible. + * There is also an MPU-401 interface. + * + * AFAIK, the following systems have an on-board Jazz16 chip: + * IBM PPS Model 6015 + * Texas Intstruments/Acer Travelmate TM5000 series. + * Please tell me if this driver works with these boards or not. + */ + +#include <sound/driver.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/core.h> +#include <sound/sb.h> +#include <sound/mpu401.h> +#include <sound/opl3.h> +#include <sound/initval.h> + +MODULE_AUTHOR("Rask Ingemann Lambertsen rask@sygehus.dk"); +MODULE_DESCRIPTION("Jazz16"); +MODULE_LICENSE("GPL"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for Jazz16 soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for Jazz16 soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable Jazz16 soundcard."); + +#define PFX "jazz16: " +#define PFX_WARN KERN_WARNING PFX +#define PFX_ERR KERN_ERR PFX + +static struct pnp_driver snd_jazz16_pnp_driver; + +static irqreturn_t jazz16_interrupt(int irq, void *card) +{ + return snd_sb8dsp_interrupt(card); +} + +static struct pnp_device_id snd_jazz16_pnpids[] = { + { .id = "PNPb00f" }, + { .id = "" } +}; +MODULE_DEVICE_TABLE(pnp, snd_jazz16_pnpids); + +static int __devinit snd_jazz16_pnp_probe(struct pnp_dev *pnp_dev, + const struct pnp_device_id *pnp_id) +{ struct snd_sb *chip; + struct snd_card *card; + struct snd_opl3 *opl3; + int err; + uint sbport, sbirq, sbdma, mpuport, mpuirq; + static uint dev_num = 0; + + if (enable[dev_num]) + card = snd_card_new(index[dev_num], id[dev_num], THIS_MODULE, 0); + else + card = NULL; + dev_num++; + if (card == NULL) + return enable[dev_num] ? -ENOMEM : -ENODEV; + snd_card_set_dev(card, &pnp_dev->dev); + pnp_set_drvdata(pnp_dev, card); + /* TODO use pnp_port_valid(), pnp_port_flags(), pnp_port_length()... */ + sbport = pnp_port_start(pnp_dev, 0); + sbirq = pnp_irq(pnp_dev, 0); + sbdma = pnp_dma(pnp_dev, 0); + err = snd_sbdsp_create(card, sbport, sbirq, jazz16_interrupt, + sbdma, -1, SB_HW_AUTO, &chip); + if (err < 0) + goto out_err; + + strcpy(card->driver, "Jazz16"); + strcpy(card->shortname, "Media Vision Jazz16"); + sprintf(card->longname, "%s at %#x, irq %u, dma %u", + chip->name, sbport, sbirq, chip->dma8); + + if (chip->hardware != SB_HW_JAZZ16) { + snd_printk(PFX_ERR "Not a Jazz16 chip at %#x.\n", sbport); + snd_card_free(card); + return -ENODEV; + } + err = snd_sb8dsp_pcm(chip, 0, NULL); + if (err < 0) + goto out_err; + + err = snd_sbmixer_new(chip); + if (err < 0) + goto out_err; + + err = snd_opl3_create(card, sbport, sbport + 2, + OPL3_HW_AUTO, 1, &opl3); + if (err < 0) + snd_printk(PFX_WARN "No OPL device found, skipping.\n"); + else { + err = snd_opl3_timer_new(opl3, 1, 2); + if (err < 0) + goto out_err; + err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); + if (err < 0) + goto out_err; + } + if (pnp_port_valid(pnp_dev, 1) + && !(pnp_port_flags(pnp_dev, 1) & IORESOURCE_DISABLED)) { + int mpuirqflags; + if (!pnp_irq_valid(pnp_dev, 1) + || pnp_irq_flags(pnp_dev, 1) & IORESOURCE_DISABLED) { + mpuirq = -1; + mpuirqflags = 0; + } else { + mpuirq = pnp_irq(pnp_dev, 1); + mpuirqflags = IRQF_DISABLED; + } + mpuport = pnp_port_start(pnp_dev, 1); + err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + mpuport, 0, mpuirq, + mpuirqflags, NULL); + if (err < 0) + snd_printk(PFX_WARN "MPU-401 device not configured, skipping.\n"); + } + err = snd_card_register(card); + if (err >= 0) + return 0; +out_err: + snd_card_free(card); + return err; +} + +static void __devexit snd_jazz16_pnp_remove(struct pnp_dev *dev) +{ + snd_card_free(pnp_get_drvdata(dev)); + pnp_set_drvdata(dev, NULL); +} + +static struct pnp_driver snd_jazz16_pnp_driver = { + .name = "Jazz16", + .id_table = snd_jazz16_pnpids, + .probe = snd_jazz16_pnp_probe, + .remove = __devexit_p(snd_jazz16_pnp_remove), +}; + +static int __devinit alsa_card_jazz16_init(void) +{ + return pnp_register_driver(&snd_jazz16_pnp_driver); +} + +static void __devexit alsa_card_jazz16_exit(void) +{ + pnp_unregister_driver(&snd_jazz16_pnp_driver); +} + +module_init(alsa_card_jazz16_init); +module_exit(alsa_card_jazz16_exit);