[alsa-devel] [PATCHv2] ALSA: Support Media Vision Jazz16 chips

Rask Ingemann Lambertsen rask at sygehus.dk
Thu Apr 12 20:22:39 CEST 2007


From: Rask Ingemann Lambertsen <rask at 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 at 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 at kss-loka.si>
  *   Cleaned up and rewrote lowlevel routines.
+ *
+ * Wed Apr 11 23:56:18 CEST 2007 Rask Ingemann Lambertsen <rask at 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 at 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);


-- 
Rask Ingemann Lambertsen


More information about the Alsa-devel mailing list